5988bb8e5e33473fa5fd1ded10b96f2fe26352db
[jalview.git] / src / jalview / gui / VamsasApplication.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import jalview.bin.Cache;
22 import jalview.datamodel.SequenceI;
23 import jalview.io.VamsasAppDatastore;
24 import jalview.structure.StructureSelectionManager;
25 import jalview.structure.VamsasListener;
26
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.io.File;
30 import java.io.IOException;
31 import java.util.Hashtable;
32 import java.util.IdentityHashMap;
33
34 import javax.swing.JInternalFrame;
35 import javax.swing.JOptionPane;
36
37 import uk.ac.vamsas.client.ClientHandle;
38 import uk.ac.vamsas.client.IClient;
39 import uk.ac.vamsas.client.IClientDocument;
40 import uk.ac.vamsas.client.InvalidSessionDocumentException;
41 import uk.ac.vamsas.client.NoDefaultSessionException;
42 import uk.ac.vamsas.client.UserHandle;
43 import uk.ac.vamsas.client.VorbaId;
44 import uk.ac.vamsas.client.picking.IMessageHandler;
45 import uk.ac.vamsas.client.picking.IPickManager;
46 import uk.ac.vamsas.client.picking.Message;
47 import uk.ac.vamsas.client.picking.MouseOverMessage;
48 import uk.ac.vamsas.objects.core.Entry;
49
50 /**
51  * @author jimp
52  * 
53  */
54 public class VamsasApplication
55 {
56   IClient vclient = null;
57
58   ClientHandle app = null;
59
60   UserHandle user = null;
61
62   Desktop jdesktop = null; // our jalview desktop reference
63
64   // Cache.preferences for vamsas client session arena
65   // preferences for check for default session at startup.
66   // user and organisation stuff.
67   public VamsasApplication(Desktop jdesktop, File sessionPath)
68   {
69     // JBPNote:
70     // we should create a session URI from the sessionPath and pass it to
71     // the clientFactory - but the vamsas api doesn't cope with that yet.
72     this.jdesktop = jdesktop;
73     initClientSession(null, sessionPath);
74   }
75
76   private static uk.ac.vamsas.client.IClientFactory getClientFactory()
77           throws IOException
78   {
79     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
80   }
81
82   /**
83    * Start a new vamsas session
84    * 
85    * @param jdesktop
86    */
87   public VamsasApplication(Desktop jdesktop)
88   {
89     this.jdesktop = jdesktop;
90     initClientSession(null, null);
91   }
92
93   /**
94    * init a connection to the session at the given url
95    * 
96    * @param jdesktop
97    * @param sessionUrl
98    */
99   public VamsasApplication(Desktop jdesktop, String sessionUrl)
100   {
101     this.jdesktop = jdesktop;
102     initClientSession(sessionUrl, null);
103   }
104
105   /**
106    * @throws IOException
107    *                 or other if clientfactory instantiation failed.
108    * @return list of current sessions or null if no session exists.
109    */
110   public static String[] getSessionList() throws Exception
111   {
112     return getClientFactory().getCurrentSessions();
113   }
114
115   /**
116    * initialise, possibly with either a valid session url or a file for a new
117    * session
118    * 
119    * @param sess
120    *                null or a valid session url
121    * @param vamsasDocument
122    *                null or a valid vamsas document file
123    * @return false if no vamsas connection was made
124    */
125   private boolean initClientSession(String sess, File vamsasDocument)
126   {
127     try
128     {
129       // Only need to tell the library what the application is here
130       app = getJalviewHandle();
131       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
132       if (vamsasDocument != null)
133       {
134         if (sess != null)
135         {
136           throw new Error(
137                   "Implementation Error - cannot import existing vamsas document into an existing session, Yet!");
138         }
139         try
140         {
141           vclient = clientfactory.openAsNewSessionIClient(app,
142                   vamsasDocument);
143         } catch (InvalidSessionDocumentException e)
144         {
145           JOptionPane
146                   .showInternalMessageDialog(
147                           Desktop.desktop,
148
149                           "VAMSAS Document could not be opened as a new session - please choose another",
150                           "VAMSAS Document Import Failed",
151                           JOptionPane.ERROR_MESSAGE);
152
153         }
154       }
155       else
156       {
157         // join existing or create a new session
158         if (sess == null)
159         {
160           vclient = clientfactory.getNewSessionIClient(app);
161         }
162         else
163         {
164           vclient = clientfactory.getIClient(app, sess);
165         }
166       }
167       // set some properties for our VAMSAS interaction
168       setVclientConfig();
169       user = vclient.getUserHandle();
170
171     } catch (Exception e)
172     {
173       jalview.bin.Cache.log
174               .error("Couldn't instantiate vamsas client !", e);
175       return false;
176     }
177     return true;
178   }
179
180   private void setVclientConfig()
181   {
182     if (vclient == null)
183     {
184       return;
185     }
186     try
187     {
188       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
189       {
190         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient) vclient)
191                 .getSimpleClientConfig();
192         cfg._validatemergedroots = false;
193         cfg._validateupdatedroots = true; // we may write rubbish otherwise.
194       }
195     } catch (Error e)
196     {
197       Cache.log
198               .warn(
199                       "Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",
200                       e);
201     } catch (Exception e)
202     {
203       Cache.log
204               .warn(
205                       "Probable VAMSAS client incompatibility - carrying on regardless",
206                       e);
207     }
208   }
209
210   /**
211    * make the appHandle for Jalview
212    * 
213    * @return
214    */
215   private ClientHandle getJalviewHandle()
216   {
217     return new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache
218             .getProperty("VERSION"));
219   }
220
221   /**
222    * 
223    * @return true if we are registered in a vamsas session
224    */
225   public boolean inSession()
226   {
227     return (vclient != null);
228   }
229
230   /**
231    * called to connect to session inits handlers, does an initial document
232    * update.
233    */
234   public void initial_update()
235   {
236     if (!inSession())
237     {
238       throw new Error(
239               "Impementation error! Vamsas Operations when client not initialised and connected.");
240     }
241     addDocumentUpdateHandler();
242     addStoreDocumentHandler();
243     startSession();
244     Cache.log
245             .debug("Jalview loading the Vamsas Session for the first time.");
246     dealWithDocumentUpdate(false); // we don't push an update out to the
247     // document yet.
248     Cache.log.debug("... finished update for the first time.");
249   }
250
251   /**
252    * Update all windows after a vamsas datamodel change. this could go on the
253    * desktop object!
254    * 
255    */
256   protected void updateJalviewGui()
257   {
258     JInternalFrame[] frames = jdesktop.getAllFrames();
259
260     if (frames == null)
261     {
262       return;
263     }
264
265     try
266     {
267       // REVERSE ORDER
268       for (int i = frames.length - 1; i > -1; i--)
269       {
270         if (frames[i] instanceof AlignFrame)
271         {
272           AlignFrame af = (AlignFrame) frames[i];
273           af.alignPanel.alignmentChanged();
274         }
275       }
276     } catch (Exception e)
277     {
278       Cache.log
279               .warn(
280                       "Exception whilst refreshing jalview windows after a vamsas document update.",
281                       e);
282     }
283   }
284
285   public void push_update()
286   {
287     Thread udthread = new Thread(new Runnable()
288     {
289
290       public void run()
291       {
292         Cache.log.info("Jalview updating to the Vamsas Session.");
293
294         dealWithDocumentUpdate(true);
295         /*
296          * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); }
297          * catch (Exception e) { Cache.log.error("Failed to get client document
298          * for update."); // RAISE A WARNING DIALOG disableGui(false); return; }
299          * updateVamsasDocument(cdoc); updateJalviewGui();
300          * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags
301          * back vclient.updateDocument(cdoc);
302          */
303         Cache.log.info("Jalview finished updating to the Vamsas Session.");
304         // TODO Auto-generated method stub
305       }
306
307     });
308     udthread.start();
309   }
310
311   public void end_session()
312   {
313     if (!inSession())
314       throw new Error("Jalview not connected to Vamsas session.");
315     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
316     try
317     {
318       if (joinedSession)
319       {
320         vclient.finalizeClient();
321         Cache.log.info("Jalview has left the session.");
322       }
323       else
324       {
325         Cache.log
326                 .warn("JV Client leaving a session that's its not joined yet.");
327       }
328       joinedSession = false;
329       vclient = null;
330       app = null;
331       user = null;
332       jv2vobj = null;
333       vobj2jv = null;
334     } catch (Exception e)
335     {
336       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
337     }
338   }
339
340   public void updateJalview(IClientDocument cdoc)
341   {
342     Cache.log.debug("Jalview updating from sesion document ..");
343     ensureJvVamsas();
344     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
345             baseProvEntry(), alRedoState);
346     vds.updateToJalview();
347     Cache.log.debug(".. finished updating from sesion document.");
348
349   }
350
351   private void ensureJvVamsas()
352   {
353     if (jv2vobj == null)
354     {
355       jv2vobj = new IdentityHashMap();
356       vobj2jv = new Hashtable();
357       alRedoState = new Hashtable();
358     }
359   }
360
361   /**
362    * jalview object binding to VorbaIds
363    */
364   IdentityHashMap jv2vobj = null;
365
366   Hashtable vobj2jv = null;
367
368   Hashtable alRedoState = null;
369
370   public void updateVamsasDocument(IClientDocument doc)
371   {
372     ensureJvVamsas();
373     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
374             baseProvEntry(), alRedoState);
375     // wander through frames
376     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
377
378     if (frames == null)
379     {
380       return;
381     }
382
383     try
384     {
385       // REVERSE ORDER
386       for (int i = frames.length - 1; i > -1; i--)
387       {
388         if (frames[i] instanceof AlignFrame)
389         {
390           AlignFrame af = (AlignFrame) frames[i];
391
392           // update alignment and root from frame.
393           vds.storeVAMSAS(af.getViewport(), af.getTitle());
394         }
395       }
396       // REVERSE ORDER
397       for (int i = frames.length - 1; i > -1; i--)
398       {
399         if (frames[i] instanceof AlignFrame)
400         {
401           AlignFrame af = (AlignFrame) frames[i];
402
403           // add any AlignedCodonFrame mappings on this alignment to any other.
404           vds.storeSequenceMappings(af.getViewport(), af.getTitle());
405         }
406       }
407     } catch (Exception e)
408     {
409       Cache.log.error("Vamsas Document store exception", e);
410     }
411   }
412
413   private Entry baseProvEntry()
414   {
415     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
416     pentry.setUser(user.getFullName());
417     pentry.setApp(app.getClientUrn());
418     pentry.setDate(new java.util.Date());
419     pentry.setAction("created");
420     return pentry;
421   }
422
423   /**
424    * do a vamsas document update or update jalview from the vamsas document
425    * 
426    * @param fromJalview
427    *                true to update from jalview to the vamsas document
428    */
429   protected void dealWithDocumentUpdate(boolean fromJalview)
430   {
431     // called by update handler for document update.
432     Cache.log.debug("Updating jalview from changed vamsas document.");
433     disableGui(true);
434     try
435     {
436       long time = System.currentTimeMillis();
437       IClientDocument cdoc = vclient.getClientDocument();
438       if (Cache.log.isDebugEnabled())
439       {
440         Cache.log.debug("Time taken to get ClientDocument = "
441                 + (System.currentTimeMillis() - time));
442         time = System.currentTimeMillis();
443       }
444       if (fromJalview)
445       {
446         this.updateVamsasDocument(cdoc);
447         if (Cache.log.isDebugEnabled())
448         {
449           Cache.log
450                   .debug("Time taken to update Vamsas Document from jalview\t= "
451                           + (System.currentTimeMillis() - time));
452           time = System.currentTimeMillis();
453         }
454         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
455         if (Cache.log.isDebugEnabled())
456         {
457           Cache.log.debug("Time taken to set Document Roots\t\t= "
458                   + (System.currentTimeMillis() - time));
459           time = System.currentTimeMillis();
460         }
461       }
462       else
463       {
464         updateJalview(cdoc);
465         if (Cache.log.isDebugEnabled())
466         {
467           Cache.log
468                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
469                           + (System.currentTimeMillis() - time));
470           time = System.currentTimeMillis();
471         }
472
473       }
474       vclient.updateDocument(cdoc);
475       if (Cache.log.isDebugEnabled())
476       {
477         Cache.log.debug("Time taken to update Session Document\t= "
478                 + (System.currentTimeMillis() - time));
479         time = System.currentTimeMillis();
480       }
481       cdoc = null;
482     } catch (Exception ee)
483     {
484       System.err.println("Exception whilst updating :");
485       ee.printStackTrace(System.err);
486     }
487     Cache.log.debug("Finished updating from document change.");
488     disableGui(false);
489   }
490
491   private void addDocumentUpdateHandler()
492   {
493     final VamsasApplication client = this;
494     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
495     {
496       public void propertyChange(PropertyChangeEvent evt)
497       {
498         Cache.log.debug("Dealing with document update event.");
499         client.dealWithDocumentUpdate(false);
500         Cache.log.debug("finished dealing with event.");
501       }
502     });
503     Cache.log.debug("Added Jalview handler for vamsas document updates.");
504   }
505
506   private void addStoreDocumentHandler()
507   {
508     final VamsasApplication client = this;
509     vclient.addVorbaEventHandler(
510             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
511             new PropertyChangeListener()
512             {
513               public void propertyChange(PropertyChangeEvent evt)
514               {
515                 Cache.log
516                         .debug("Asking user if the vamsas session should be stored.");
517                 int reply = JOptionPane
518                         .showInternalConfirmDialog(
519                                 Desktop.desktop,
520                                 "The current VAMSAS session has unsaved data - do you want to save it ?",
521                                 "VAMSAS Session Shutdown",
522                                 JOptionPane.YES_NO_OPTION,
523                                 JOptionPane.QUESTION_MESSAGE);
524
525                 if (reply == JOptionPane.YES_OPTION)
526                 {
527                   Cache.log.debug("Prompting for vamsas store filename.");
528                   Desktop.instance.vamsasSave_actionPerformed(null);
529                   Cache.log.debug("Finished attempt at storing document.");
530                 }
531                 Cache.log
532                         .debug("finished dealing with REQUESTTOCLOSE event.");
533               }
534             });
535     Cache.log.debug("Added Jalview handler for vamsas document updates.");
536   }
537
538   public void disableGui(boolean b)
539   {
540     Desktop.instance.setVamsasUpdate(b);
541   }
542
543   private boolean joinedSession = false;
544
545   private VamsasListener picker = null;
546
547   private void startSession()
548   {
549     if (inSession())
550     {
551       try
552       {
553         vclient.joinSession();
554         joinedSession = true;
555       } catch (Exception e)
556       {
557         // Complain to GUI
558         Cache.log.error("Failed to join vamsas session.", e);
559         vclient = null;
560       }
561       try
562       {
563         final IPickManager pm = vclient.getPickManager();
564         final StructureSelectionManager ssm = StructureSelectionManager
565                 .getStructureSelectionManager();
566         pm.registerMessageHandler(new IMessageHandler()
567         {
568           String last = null;
569
570           public void handleMessage(Message message)
571           {
572             if (message instanceof MouseOverMessage && vobj2jv != null)
573             {
574               MouseOverMessage mm = (MouseOverMessage) message;
575               String mstring = mm.getVorbaID() + " " + mm.getPosition();
576               if (last != null && mstring.equals(last))
577               {
578                 return;
579               }
580               // if (Cache.log.isDebugEnabled())
581               // {
582               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
583               // "+mm.getPosition());
584               // }
585               Object jvobj = vobj2jv.get(mm.getVorbaID());
586               if (jvobj != null && jvobj instanceof SequenceI)
587               {
588                 last = mstring;
589                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
590                 // bound to "+jvobj+" at "+mm.getPosition());
591                 // position is character position in aligned sequence
592                 ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
593                         .getPosition());
594               }
595             }
596           }
597         });
598         picker = new VamsasListener()
599         {
600           SequenceI last = null;
601
602           int i = -1;
603
604           public void mouseOver(SequenceI seq, int index)
605           {
606             if (jv2vobj == null)
607               return;
608             if (seq != last || i != index)
609             {
610               VorbaId v = (VorbaId) jv2vobj.get(seq);
611               if (v != null)
612               {
613                 Cache.log.debug("Mouse over " + v.getId() + " bound to "
614                         + seq + " at " + index);
615                 last = seq;
616                 i = index;
617                 MouseOverMessage message = new MouseOverMessage(v.getId(),
618                         index);
619                 pm.sendMessage(message);
620               }
621             }
622           }
623         };
624         ssm.addStructureViewerListener(picker); // better method here
625       } catch (Exception e)
626       {
627         Cache.log.error("Failed to init Vamsas Picking", e);
628       }
629     }
630   }
631 }