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