improved javascript callback execution thread and provided 'large string' asynchronou...
[jalview.git] / src / jalview / javascript / JSFunctionExec.java
1 package jalview.javascript;\r
2 \r
3 import java.net.URL;\r
4 import java.util.Vector;\r
5 \r
6 import netscape.javascript.JSException;\r
7 import netscape.javascript.JSObject;\r
8 import jalview.bin.JalviewLite;\r
9 \r
10 public class JSFunctionExec implements Runnable\r
11 {\r
12   JalviewLite jvlite;\r
13 \r
14   public JSFunctionExec(JalviewLite applet)\r
15   {\r
16     jvlite = applet;\r
17     \r
18     jsExecQueue = jvlite.getJsExecQueue();\r
19     jvlite.setExecutor(this);\r
20   }\r
21 \r
22   private Vector jsExecQueue;\r
23 \r
24   private Thread executor = null;\r
25 \r
26   public void stopQueue()\r
27   {\r
28     if (jsExecQueue != null)\r
29     {\r
30       synchronized (jsExecQueue)\r
31       {\r
32         Vector q = jsExecQueue;\r
33         q.removeAllElements();\r
34         jsExecQueue = null;\r
35         synchronized (q)\r
36         {\r
37           q.notifyAll();\r
38         }\r
39       }\r
40       executor = null;\r
41     }\r
42   }\r
43 \r
44   public void run()\r
45   {\r
46     while (jsExecQueue != null)\r
47     {\r
48       if (jsExecQueue.size() > 0)\r
49       {\r
50         Runnable r = (Runnable) jsExecQueue.elementAt(0);\r
51         jsExecQueue.removeElementAt(0);\r
52         try\r
53         {\r
54           r.run();\r
55         } catch (Exception ex)\r
56         {\r
57           ex.printStackTrace();\r
58         } catch (Error ex)\r
59         {\r
60           ex.printStackTrace();\r
61         }\r
62       }\r
63       else\r
64       {\r
65         try\r
66         {\r
67           synchronized (jsExecQueue)\r
68           {\r
69             jsExecQueue.wait(1000);\r
70           }\r
71         } catch (Exception ex)\r
72         {\r
73         }\r
74         ;\r
75       }\r
76     }\r
77 \r
78   }\r
79 \r
80   /**\r
81    * execute a javascript callback synchronously\r
82    * \r
83    * @param _listener\r
84    * @param objects\r
85    * @throws Exception\r
86    */\r
87   public void executeJavascriptFunction(final String _listener,\r
88           final Object[] objects) throws Exception\r
89   {\r
90     executeJavascriptFunction(false, _listener, objects);\r
91   }\r
92 \r
93   /**\r
94    * execute a javascript callback synchronously or asynchronously\r
95    * \r
96    * @param async\r
97    *          - true to execute asynchronously (do this for gui events)\r
98    * @param _listener\r
99    *          - javascript function\r
100    * @param objects\r
101    *          - arguments\r
102    * @throws Exception\r
103    *           - only if call is synchronous\r
104    */\r
105   public void executeJavascriptFunction(final boolean async,\r
106           final String _listener, Object[] arguments) throws Exception\r
107   {\r
108 \r
109     executeJavascriptFunction(async, _listener, arguments, null);\r
110 \r
111   }\r
112 \r
113   public void executeJavascriptFunction(final boolean async,\r
114           final String _listener, Object[] arguments, final String dbgMsg)\r
115           throws Exception\r
116   {\r
117     final Object[] objects = new Object[arguments != null ? arguments.length\r
118             : 0];\r
119     if (arguments != null)\r
120     {\r
121       System.arraycopy(arguments, 0, objects, 0, arguments.length);\r
122     }\r
123     final Exception[] jsex = new Exception[1];\r
124     Runnable exec = new Runnable()\r
125     {\r
126       public void run()\r
127       {\r
128         try\r
129         {\r
130           JSObject scriptObject = null;\r
131           try\r
132           {\r
133             scriptObject = JSObject.getWindow(jvlite);\r
134           } catch (Exception ex)\r
135           {\r
136           }\r
137           ;\r
138           if (scriptObject != null)\r
139           {\r
140             if (jvlite.debug && dbgMsg != null)\r
141             {\r
142               System.err.println(dbgMsg);\r
143             }\r
144             scriptObject.call(_listener, objects);\r
145           }\r
146         } catch (Exception jex)\r
147         {\r
148           // squash any malformedURLExceptions thrown by windows/safari\r
149           if (!(jex instanceof java.net.MalformedURLException))\r
150           {\r
151             if (jvlite.debug)\r
152             {\r
153               System.err.println(jex);\r
154             }\r
155             if (jex instanceof netscape.javascript.JSException && jvlite.jsfallbackEnabled)\r
156             {\r
157               jsex[0] = (netscape.javascript.JSException) jex;\r
158               if (jvlite.debug)\r
159               {\r
160                 System.err.println("Falling back to javascript: url call");\r
161               }\r
162               StringBuffer sb = new StringBuffer("javascript:" + _listener\r
163                       + "(");\r
164               for (int i = 0; objects != null && i < objects.length; i++)\r
165               {\r
166                 if (i > 0)\r
167                 {\r
168                   sb.append(",");\r
169                 }\r
170                 sb.append("\"");\r
171                 // strip out nulls and complex objects that we can't pass this\r
172                 // way.\r
173                 if (objects[i] != null\r
174                         && !(objects[i].getClass().getName()\r
175                                 .indexOf("jalview") == 0))\r
176                 {\r
177                   sb.append(objects[i].toString());\r
178                 }\r
179                 sb.append("\"");\r
180               }\r
181               sb.append(")");\r
182               if (jvlite.debug)\r
183               {\r
184                 System.err.println(sb.toString());\r
185               }\r
186               // alternate\r
187               URL url = null;\r
188               try\r
189               {\r
190                 url = new URL(sb.toString());\r
191                 jvlite.getAppletContext().showDocument(url);\r
192                 jex = null;\r
193               } catch (Exception uex)\r
194               {\r
195                 jex = uex;\r
196               }\r
197             }\r
198             if (jex != null)\r
199             {\r
200               if (async)\r
201               {\r
202                 jex.printStackTrace();\r
203               }\r
204               else\r
205               {\r
206                 jsex[0] = jex;\r
207               }\r
208             }\r
209             ;\r
210           }\r
211 \r
212         }\r
213       }\r
214     };\r
215     if (async)\r
216     {\r
217       if (executor == null)\r
218       {\r
219         executor = new Thread(new JSFunctionExec(jvlite));\r
220         executor.start();\r
221       }\r
222       synchronized (jsExecQueue)\r
223       {\r
224         jsExecQueue.addElement(exec);\r
225         jsExecQueue.notify();\r
226       }\r
227     }\r
228     else\r
229     {\r
230       // wat for executor to notify us if it's running.\r
231       exec.run();\r
232       if (jsex[0] != null)\r
233       {\r
234         throw (jsex[0]);\r
235       }\r
236     }\r
237   }\r
238 \r
239 }\r