2003/06/03 00:53:06
[org.ibex.core.git] / src / org / xwt / XWT.java
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import java.io.*;
5 import java.net.*;
6 import java.text.*;
7 import java.util.*;
8 import org.xwt.js.*;
9 import org.xwt.util.*;
10 import org.bouncycastle.util.encoders.Base64;
11
12 /** Singleton class that provides all functionality in the xwt.* namespace */
13 public final class XWT extends JS.Obj {
14
15     public static final XWT singleton = new XWT();
16
17     /** each key is a string representing a filename which the user has already given XWT permission to write to */
18     private static Hashtable safeFiles = new Hashtable();
19
20     public Object get(Object name) {
21         if (name.equals("alt")) return Surface.alt ? Boolean.TRUE : Boolean.FALSE;
22         else if (name.equals("control")) return Surface.control ? Boolean.TRUE : Boolean.FALSE;
23         else if (name.equals("shift")) return Surface.shift ? Boolean.TRUE : Boolean.FALSE;
24         else if (name.equals("clipboard")) return Platform.getClipBoard();
25         else if (name.equals("static")) return Static.getStatic("");
26         else if (name.equals("button")) {
27             if (Surface.button1 && !Surface.button2 && !Surface.button3) return new Integer(1);
28             else if (!Surface.button1 && Surface.button2 && !Surface.button3) return new Integer(1);
29             else if (!Surface.button1 && !Surface.button2 && Surface.button3) return new Integer(1);
30             else return new Integer(0);
31         }
32         else if (name.equals("encodeURI")) throw new Error("not implemented");
33         else if (name.equals("encodeURIComponent")) throw new Error("not implemented");
34         else if (name.equals("decodeURI")) throw new Error("not implemented");
35         else if (name.equals("decodeURIComponent")) throw new Error("not implemented");
36         else return super.get(name);
37     }
38
39     public void put(Object name, Object value) {
40         if (name.equals("thread") && value != null && value instanceof JS.Function) ThreadMessage.newthread((JS.Function)value);
41         else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
42         else if (name.equals("proxyAuthorization")) {
43             // FIXME: undocumented, possibly insecure
44             Proxy.Authorization.authorization = value.toString();
45             Proxy.Authorization.waitingForUser.release();
46         } else super.put(name, value);
47     }
48
49     private XWT() {
50         put("maxdim", new Integer(Short.MAX_VALUE));
51         put("origin", Main.origin);
52         put("altKeyName", Platform.altKeyName());
53         put("screenWidth", new Integer(Platform.getScreenWidth()));
54         put("screenHeight", new Integer(Platform.getScreenHeight()));
55         put("fileSeparator", File.separator);
56         put("homeDir", System.getProperty("user.home"));
57         put("tempDir", System.getProperty("java.io.tempdir"));
58
59         put("math", new JS.Obj() { public Object get(Object name) {
60             if ("ceil".equals(name)) return new JS.Function() { public Object _call(JS.Array args)
61                     { if (args.elementAt(0) == null) return null;
62                     return new Long((long)Math.ceil(Double.parseDouble(args.elementAt(0).toString()))); } };
63             else if ("floor".equals(name)) return new JS.Function() { public Object _call(JS.Array args)
64                     { if (args.elementAt(0) == null) return null;
65                         return new Long((long)Math.floor(Double.parseDouble(args.elementAt(0).toString()))); } };
66             else if ("round".equals(name)) return new JS.Function() { public Object _call(JS.Array args)
67                     { if (args.elementAt(0) == null) return null;
68                         return new Long((long)Math.round(Double.parseDouble(args.elementAt(0).toString()))); } };
69             else if ("abs".equals(name)) return new JS.Function() { public Object _call(JS.Array args)
70                     { if (args.elementAt(0) == null) return null;
71                         return new Long((long)Math.abs(Double.parseDouble(args.elementAt(0).toString()))); } };
72             else if ("min".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
73                 if (args.length() < 2 || args.elementAt(0) == null || args.elementAt(1) == null) return args.elementAt(0);
74                 return new Double(Math.min(((Number)args.elementAt(0)).doubleValue(),
75                                            ((Number)args.elementAt(1)).doubleValue())); } };
76             else if ("max".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
77                 if (args.length() < 2) return args.elementAt(0);
78                 return new Double(Math.max(((Number)args.elementAt(0)).doubleValue(),
79                                            ((Number)args.elementAt(1)).doubleValue())); } };
80             else if ("cos".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
81                 return new Double(Math.cos(((Number)args.elementAt(0)).doubleValue())); } };
82             else if ("sin".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
83                 return new Double(Math.sin(((Number)args.elementAt(0)).doubleValue())); } };
84             else if ("tan".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
85                 return new Double(Math.tan(((Number)args.elementAt(0)).doubleValue())); } };
86             else if ("acos".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
87                 return new Double(Math.acos(((Number)args.elementAt(0)).doubleValue())); } };
88             else if ("asin".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
89                 return new Double(Math.asin(((Number)args.elementAt(0)).doubleValue())); } };
90             else if ("atan".equals(name)) return new JS.Function() { public Object _call(JS.Array args) {
91                 return new Double(Math.atan(((Number)args.elementAt(0)).doubleValue())); } };
92             return null;
93         }});
94
95         put("newBrowserWindow", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
96             if (args.length() != 1 || args.elementAt(0) == null) return null;
97             Platform.newBrowserWindow(args.elementAt(0).toString());
98             return null;
99         }});
100
101         put("parseFloat", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
102             if (args.length() != 1 || args.elementAt(0) == null) return null;
103             return new Float(args.elementAt(0).toString());
104         }});
105
106         put("parseInt", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
107             if (args.length() != 1 || args.elementAt(0) == null) return null;
108             return new Float(args.elementAt(0).toString());
109         }});
110
111         put("yield", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
112             sleep(0);
113             return null;
114         }});
115
116         put("theme", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
117                 if (args.length() != 2) return null;
118                 if (args.elementAt(0) == null || args.elementAt(1) == null) return null;
119                 for(int i=1; i<args.length(); i++) {
120                     if (args.elementAt(i) instanceof String) {
121                         String from = (String)args.elementAt(0);
122                         String to = (String)args.elementAt(i);
123                         if (Log.on) Log.log(this, "retheming from " + from + " to " + to);
124                         Resources.mapFrom.addElement(from);
125                         Resources.mapTo.addElement(to);
126                     }
127                 }
128                 JS.Function callback = args.elementAt(args.length() - 1) instanceof Function ?
129                     (Function)args.elementAt(args.length() - 1) : null;
130                 Template.retheme(callback);
131                 return null;
132         }});
133             
134         put("println", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
135             if (args.length() != 1) return null;
136             if (Log.on) Log.log(this, JS.getFileAndLine() + " " +
137                                 (args.elementAt(0) == null ? "**null**" : args.elementAt(0).toString()));
138             return null;
139         }});
140
141         put("date", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
142             Log.log(XWT.class, "date not implemented");
143             //throw new Error("not implemented");
144             return null;
145         }});
146
147         put("regexp", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
148             //throw new Error("not implemented");
149             Log.log(XWT.class, "regexp not implemented");
150             return null;
151         }});
152
153         put("listfonts", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
154             Object[] fonts = Platform.listFonts();
155             JS.Array ret = new JS.Array();
156             for(int i=0; i<fonts.length; i++) ret.addElement(fonts[i]);
157             return ret;
158         }});
159
160         put("xmlrpc", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
161             if (args.length() != 1 || args.elementAt(0) == null) return null;
162             return new XMLRPC(args.elementAt(0).toString(), "");
163         }});
164
165         put("soap", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
166             if (args.length() == 1 && args.elementAt(0) != null) return new SOAP(args.elementAt(0).toString(), "", null, null);
167             else if (args.length() == 2 && args.elementAt(0) != null && args.elementAt(1) != null)
168                 return new SOAP(args.elementAt(0).toString(), "", args.elementAt(1).toString(), null);
169             else if (args.length() == 3 && args.elementAt(0) != null && args.elementAt(1) != null && args.elementAt(2) != null)
170                 return new SOAP(args.elementAt(0).toString(), "", args.elementAt(1).toString(), args.elementAt(2).toString());
171             else return null;
172         }});
173
174         put("textwidth", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
175             if (args.length() < 1 || args.length() > 2) return null;
176             if (args.elementAt(0) == null || (args.length() == 2 && args.elementAt(1) == null)) return null;
177             String font = args.length() == 1 ? Platform.getDefaultFont() : args.elementAt(0).toString();
178             String text = args.length() == 1 ? args.elementAt(0).toString() : args.elementAt(1).toString();
179             XWF xwf = XWF.getXWF(font);
180             if (xwf == null) return new Integer(Platform.stringWidth(font, text));
181             else return new Integer(xwf.stringWidth(text));
182         }});
183
184         put("textheight", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
185             if (args.length() > 1) return null;
186             if (args.length() == 1 && args.elementAt(0) == null) return null;
187             String font = args.length() == 0 || args.elementAt(0) == null ? Platform.getDefaultFont() : args.elementAt(0).toString();
188             XWF xwf = XWF.getXWF(font);
189             if (xwf == null) return new Integer(Platform.getMaxAscent(font) + Platform.getMaxDescent(font));
190             else return new Integer(xwf.getMaxAscent() + xwf.getMaxDescent());
191         }});
192         
193         put("newBox", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
194             if (args.length() > 0) Log.log(XWT.class, "DEPRECATED: xwt.newBox() with multiple arguments is deprecated; use xwt.newBox().apply()");
195             JS.Function callback = null;
196             for(int i=1; i<args.length(); i++)
197                 if (args.elementAt(i) instanceof JS.Function && callback == null)
198                     callback = (JS.Function)args.elementAt(i);
199             Box ret = new Box(args.length() == 0 || args.elementAt(0) == null ? "box" : args.elementAt(0).toString(),
200                               Template.defaultImportList, callback);
201             for(int i=1; i<args.length(); i++)
202                 if (args.elementAt(i) instanceof Box)
203                     ret.put(ret.numChildren(), (Box)args.elementAt(i));
204             for(int i=1; i<args.length(); i++)
205                 if (args.elementAt(i) instanceof JS && !(args.elementAt(i) instanceof Box) && !(args.elementAt(i) instanceof JS.Function)) {
206                     JS s = (JS)args.elementAt(i);
207                     Object[] keys = s.keys();
208                     for(int j=0; j<keys.length; j++) ret.put(keys[j].toString(), s.get(keys[j].toString()));
209                 }
210             return ret;
211         }});
212
213         put("sleep", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
214             if (args != null && (args.length() != 1 || args.elementAt(0) == null)) return null;
215             int i = args == null ? 0 : SpecialBoxProperty.stoi(args.elementAt(0).toString());
216             sleep(i);
217             return null;
218         }});
219
220         put("openFile", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
221             if (args.length() != 1) return null;
222             String file = Platform.fileDialog(args.elementAt(0).toString(), false);
223             return file == null ? null : new ByteStream(file);
224         }});
225
226         put("saveFile", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
227             if (args.length() != 2) return null;
228             if (!(args.elementAt(1) instanceof ByteStream)) return null;
229             String file = args.elementAt(0).toString();
230             if (safeFiles.get(Platform.isCaseSensitive() ? file : file.toLowerCase()) == null) {
231                 file = Platform.fileDialog(file, true);
232                 if (file == null) return null;
233                 safeFiles.put(Platform.isCaseSensitive() ? file : file.toLowerCase(), new Object());
234             }
235             try {
236                 ((ByteStream)args.elementAt(1)).writeTo(new FileOutputStream(file));
237                 return null;
238             } catch (IOException e) {
239                 if (Log.on) Log.log(ByteStream.class, "IO Exception while writing a ByteStream to a file");
240                 if (Log.on) Log.log(ByteStream.class, e);
241                 throw new JS.Exn("error while writing a ByteStream to a file");
242             }
243         }});
244
245         put("saveFileAs", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
246             if (args.length() != 2) return null;
247             if (!(args.elementAt(1) instanceof ByteStream)) return null;
248             String file = args.elementAt(0).toString();
249             file = Platform.fileDialog(file, true);
250             if (file == null) return null;
251             safeFiles.put(Platform.isCaseSensitive() ? file : file.toLowerCase(), new Object());
252             try {
253                 ((ByteStream)args.elementAt(1)).writeTo(new FileOutputStream(file));
254                 return null;
255             } catch (IOException e) {
256                 if (Log.on) Log.log(ByteStream.class, "IO Exception while writing a ByteStream to a file");
257                 if (Log.on) Log.log(ByteStream.class, e);
258                 throw new JS.Exn("error while writing a ByteStream to a file");
259             }
260         }});
261
262         put("utfEncode", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
263             if (args == null || args.length() != 1) return null;
264             return new ByteStream(args.elementAt(0).toString().getBytes());
265         }});
266
267         put("parseHTML", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
268                 if (args == null || args.length() != 1 || args.elementAt(0) == null) return null;
269                 try {
270                     if (args.elementAt(0) instanceof ByteStream) {
271                         return HTML.parseReader(new InputStreamReader(((ByteStream)args.elementAt(0)).getInputStream()));
272                     } else {
273                         return HTML.parseReader(new StringReader(args.elementAt(0).toString()));
274                     }
275                 } catch (IOException e) {
276                     if (Log.on) Log.log(HTML.class, "IO Exception while parsing HTML");
277                     if (Log.on) Log.log(HTML.class, e);
278                     throw new JS.Exn("error while parsing HTML");
279                 }
280             }
281         });
282     
283     put("recursivePrintObject", new JS.Function() { public Object _call(JS.Array args) {
284         if (args.length() != 1) return null;
285         recurse("", "", args.elementAt(0));
286         return null;
287     }});
288
289     put("loadArchive", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
290         if (!ThreadMessage.suspendThread()) return null;
291         try {
292             if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
293             URL u = new URL(args.elementAt(0).toString());
294             
295             JS.Function callback = null;
296             if (args.length() == 2 && args.elementAt(1) != null && args.elementAt(1) instanceof JS.Function)
297                 callback = (JS.Function)args.elementAt(1);
298             
299             if (!u.getFile().endsWith(".xwar")) {
300                 if (Log.on) Log.log(this, "Error: archive names must end with .xwar: " + u.getFile());
301                 throw new JS.Exn("Error: archive names must end with .xwar: " + u.getFile());
302             }
303             
304             if (u.getProtocol().equals("http")) {
305                 HTTP http = new HTTP(u.toString());
306                 if (Main.originAddr == null) {
307                     try {
308                         Main.originAddr = InetAddress.getByName(u.getHost());
309                     } catch (UnknownHostException e) {
310                         if (Log.on) Log.log(this, "couldn't resolve " + u.getHost() + "; proceeding without permissions");
311                         Main.originAddr = InetAddress.getByName("0.0.0.0");
312                     }
313                 } else {
314                     Main.originAddr = InetAddress.getByName("0.0.0.0");
315                 }
316                 HTTP.HTTPInputStream in = http.GET();
317                 Resources.loadArchive(in, in.getContentLength(), callback);
318                 
319             } else if (u.getProtocol().equals("file")) {
320                 if (Main.originAddr != null) {
321                     if (Log.on) Log.log(this, "scripts downloaded from the network may not load xwars from the local filesystem");
322                     throw new JS.Exn("scripts downloaded from the network may not load xwars from the local filesystem");
323                 }
324                 Resources.loadArchive(new FileInputStream(u.getFile()), (int)new File(u.getFile()).length(), callback);
325                 
326             } else {
327                 if (Log.on) Log.log(this, "unknown protocol \"" + u.getProtocol() + "\"");
328                 throw new JS.Exn("unknown protocol \"" + u.getProtocol() + "\"");
329             }
330             
331         } catch (MalformedURLException me) {
332             if (Log.on) Log.log(this, "Malformed URL: " + args.elementAt(0));
333             if (Log.on) Log.log(this, me);
334             throw new JS.Exn(me.toString());
335             
336         } catch (IOException ioe) {
337             if (Log.on) Log.log(this, "IOException while loading archive:");
338             if (Log.on) Log.log(this, ioe);
339             throw new JS.Exn(ioe.toString());
340             
341         } finally {
342             ThreadMessage.resumeThread();
343             
344         }
345         return null;
346     }});
347
348     put("prefetchImage", new JS.Function() { public Object _call(JS.Array args) throws JS.Exn {
349         if (args == null || args.length() < 1 || args.elementAt(0) == null) return null;
350         Box.getImage(args.elementAt(0).toString(),
351                      args.length() > 1 && args.elementAt(1) instanceof JS.Function ? (JS.Function)args.elementAt(1) : null);
352         return null;
353     }});
354     }
355
356     private static void recurse(String indent, String name, Object o) {
357         if (!name.equals("")) name += " : ";
358
359         if (o == null) {
360             Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<null>");
361
362         } else if (o instanceof JS.Array) {
363             Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<array>");
364             JS.Array na = (JS.Array)o;
365             for(int i=0; i<na.length(); i++)
366                 recurse(indent + "  ", i + "", na.elementAt(i));
367
368         } else if (o instanceof JS) {
369             Log.log(JS.getCurrentFunction().getSourceName(), indent + name + "<object>");
370             JS s = (JS)o;
371             Object[] keys = s.keys();
372             for(int i=0; i<keys.length; i++)
373                 recurse(indent + "  ", keys[i].toString(),
374                         (keys[i] instanceof Integer) ?
375                         s.get(((Integer)keys[i])) : s.get(keys[i].toString()));
376
377         } else {
378             Log.log(JS.getCurrentFunction().getSourceName(), indent + name + o);
379
380         }
381     }
382
383
384     public static void sleep(int i) {
385         Thread thread = Thread.currentThread();
386         if (!(thread instanceof ThreadMessage)) {
387             if (Log.on) Log.log(XWT.class, "cannot sleep() or yield() in the foreground thread");
388         } else {
389             ThreadMessage mythread = (ThreadMessage)thread;
390             mythread.done.release();
391             if (i > 0) try { Thread.sleep(i); } catch (Exception e) { }
392             MessageQueue.add(mythread);
393             mythread.go.block();
394         }
395     }
396 }
397
398
399
400
401
402
403
404