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