09036e65fe14d8be8ba1e66556fe30059d11c2b0
[jalview.git] / src / jalview / javascript / JSFunctionExec.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.javascript;
19
20 import java.net.URL;
21 import java.util.Vector;
22
23 import netscape.javascript.JSObject;
24 import jalview.bin.JalviewLite;
25
26 public class JSFunctionExec implements Runnable
27 {
28   public JalviewLite jvlite;
29
30   public JSFunctionExec(JalviewLite applet)
31   {
32     jvlite = applet;
33
34     jsExecQueue = jvlite.getJsExecQueue();
35     jvlite.setExecutor(this);
36   }
37
38   public void finalize()
39   {
40     jvlite = null;
41     executor = null;
42     if (jsExecQueue != null)
43     {
44       jsExecQueue.clear();
45     }
46     jsExecQueue = null;
47   }
48
49   private Vector jsExecQueue;
50
51   private Thread executor = null;
52
53   public void stopQueue()
54   {
55     if (jsExecQueue != null)
56     {
57       Vector<JSFunctionExec> q = null;
58       synchronized (jsExecQueue)
59       {
60         q = jsExecQueue;
61         jsExecQueue = null;
62       }
63       if (q != null)
64       {
65         for (JSFunctionExec jx : q)
66         {
67           jx.jvlite = null;
68
69         }
70         q.removeAllElements();
71         synchronized (q)
72         {
73           q.notifyAll();
74         }
75       }
76     }
77     jvlite = null;
78     executor = null;
79   }
80
81   public void run()
82   {
83     while (jsExecQueue != null)
84     {
85       if (jsExecQueue.size() > 0)
86       {
87         Runnable r = (Runnable) jsExecQueue.elementAt(0);
88         jsExecQueue.removeElementAt(0);
89         try
90         {
91           r.run();
92         } catch (Exception ex)
93         {
94           ex.printStackTrace();
95         } catch (Error ex)
96         {
97           ex.printStackTrace();
98         }
99       }
100       else
101       {
102         try
103         {
104           synchronized (jsExecQueue)
105           {
106             jsExecQueue.wait(1000);
107           }
108         } catch (Exception ex)
109         {
110         }
111         ;
112       }
113     }
114
115   }
116
117   /**
118    * execute a javascript callback synchronously
119    * 
120    * @param _listener
121    * @param objects
122    * @throws Exception
123    */
124   public void executeJavascriptFunction(final String _listener,
125           final Object[] objects) throws Exception
126   {
127     executeJavascriptFunction(false, _listener, objects);
128   }
129
130   /**
131    * execute a javascript callback synchronously or asynchronously
132    * 
133    * @param async
134    *          - true to execute asynchronously (do this for gui events)
135    * @param _listener
136    *          - javascript function
137    * @param objects
138    *          - arguments
139    * @throws Exception
140    *           - only if call is synchronous
141    */
142   public void executeJavascriptFunction(final boolean async,
143           final String _listener, Object[] arguments) throws Exception
144   {
145
146     executeJavascriptFunction(async, _listener, arguments, null);
147
148   }
149
150   public void executeJavascriptFunction(final boolean async,
151           final String _listener, Object[] arguments, final String dbgMsg)
152           throws Exception
153   {
154     final Object[] objects = new Object[arguments != null ? arguments.length
155             : 0];
156     if (arguments != null)
157     {
158       System.arraycopy(arguments, 0, objects, 0, arguments.length);
159     }
160     final Exception[] jsex = new Exception[1];
161     Runnable exec = new Runnable()
162     {
163       public void run()
164       {
165         try
166         {
167           JSObject scriptObject = null;
168           try
169           {
170             scriptObject = JSObject.getWindow(jvlite);
171           } catch (Exception ex)
172           {
173           }
174           ;
175           if (scriptObject != null)
176           {
177             if (jvlite.debug && dbgMsg != null)
178             {
179               System.err.println(dbgMsg);
180             }
181             scriptObject.call(_listener, objects);
182           }
183         } catch (Exception jex)
184         {
185           // squash any malformedURLExceptions thrown by windows/safari
186           if (!(jex instanceof java.net.MalformedURLException))
187           {
188             if (jvlite.debug)
189             {
190               System.err.println(jex);
191             }
192             if (jex instanceof netscape.javascript.JSException
193                     && jvlite.jsfallbackEnabled)
194             {
195               jsex[0] = (netscape.javascript.JSException) jex;
196               if (jvlite.debug)
197               {
198                 System.err.println("Falling back to javascript: url call");
199               }
200               StringBuffer sb = new StringBuffer("javascript:" + _listener
201                       + "(");
202               for (int i = 0; objects != null && i < objects.length; i++)
203               {
204                 if (i > 0)
205                 {
206                   sb.append(",");
207                 }
208                 sb.append("\"");
209                 // strip out nulls and complex objects that we can't pass this
210                 // way.
211                 if (objects[i] != null
212                         && !(objects[i].getClass().getName()
213                                 .indexOf("jalview") == 0))
214                 {
215                   sb.append(objects[i].toString());
216                 }
217                 sb.append("\"");
218               }
219               sb.append(")");
220               if (jvlite.debug)
221               {
222                 System.err.println(sb.toString());
223               }
224               // alternate
225               URL url = null;
226               try
227               {
228                 url = new URL(sb.toString());
229                 jvlite.getAppletContext().showDocument(url);
230                 jex = null;
231               } catch (Exception uex)
232               {
233                 jex = uex;
234               }
235             }
236             if (jex != null)
237             {
238               if (async)
239               {
240                 jex.printStackTrace();
241               }
242               else
243               {
244                 jsex[0] = jex;
245               }
246             }
247             ;
248           }
249
250         }
251       }
252     };
253     if (async)
254     {
255       if (executor == null)
256       {
257         executor = new Thread(new JSFunctionExec(jvlite));
258         executor.start();
259       }
260       synchronized (jsExecQueue)
261       {
262         jsExecQueue.addElement(exec);
263         jsExecQueue.notify();
264       }
265     }
266     else
267     {
268       // wat for executor to notify us if it's running.
269       exec.run();
270       if (jsex[0] != null)
271       {
272         throw (jsex[0]);
273       }
274     }
275   }
276
277 }