6967bac14878d6f7163861fed8e5c9586f1606b4
[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       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
188       {
189         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient)  vclient).getSimpleClientConfig();
190         cfg._validatemergedroots = false;
191         cfg._validateupdatedroots= true; //we may write rubbish otherwise. 
192       }
193     }
194     catch (Error e)
195     {
196       Cache.log.warn("Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",e);
197     }
198     catch (Exception e)
199     {
200       Cache.log.warn("Probable VAMSAS client incompatibility - carrying on regardless",e);
201     }
202   }
203
204   /**
205    * make the appHandle for Jalview
206    * 
207    * @return
208    */
209   private ClientHandle getJalviewHandle()
210   {
211     return new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache
212             .getProperty("VERSION"));
213   }
214
215   /**
216    * 
217    * @return true if we are registered in a vamsas session
218    */
219   public boolean inSession()
220   {
221     return (vclient != null);
222   }
223
224   /**
225    * called to connect to session inits handlers, does an initial document
226    * update.
227    */
228   public void initial_update()
229   {
230     if (!inSession())
231     {
232       throw new Error(
233               "Impementation error! Vamsas Operations when client not initialised and connected.");
234     }
235     addDocumentUpdateHandler();
236     addStoreDocumentHandler();
237     startSession();
238     Cache.log
239             .debug("Jalview loading the Vamsas Session for the first time.");
240     dealWithDocumentUpdate(false); // we don't push an update out to the
241                                     // document yet.
242     Cache.log.debug("... finished update for the first time.");
243   }
244
245   /**
246    * Update all windows after a vamsas datamodel change. this could go on the
247    * desktop object!
248    * 
249    */
250   protected void updateJalviewGui()
251   {
252     JInternalFrame[] frames = jdesktop.getAllFrames();
253
254     if (frames == null)
255     {
256       return;
257     }
258
259     try
260     {
261       // REVERSE ORDER
262       for (int i = frames.length - 1; i > -1; i--)
263       {
264         if (frames[i] instanceof AlignFrame)
265         {
266           AlignFrame af = (AlignFrame) frames[i];
267           af.alignPanel.alignmentChanged();
268         }
269       }
270     } catch (Exception e)
271     {
272       Cache.log
273               .warn(
274                       "Exception whilst refreshing jalview windows after a vamsas document update.",
275                       e);
276     }
277   }
278
279   public void push_update()
280   {
281     Thread udthread = new Thread(new Runnable() {
282
283       public void run()
284       {
285         Cache.log.info("Jalview updating to the Vamsas Session.");
286     
287         dealWithDocumentUpdate(true);
288         /*
289          * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); }
290          * catch (Exception e) { Cache.log.error("Failed to get client document for
291          * update."); // RAISE A WARNING DIALOG disableGui(false); return; }
292          * updateVamsasDocument(cdoc); updateJalviewGui();
293          * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags
294          * back vclient.updateDocument(cdoc);
295          */
296         Cache.log.info("Jalview finished updating to the Vamsas Session.");
297         // TODO Auto-generated method stub
298       }
299       
300     });
301     udthread.start();
302   }
303
304   public void end_session()
305   {
306     if (!inSession())
307       throw new Error("Jalview not connected to Vamsas session.");
308     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
309     try
310     {
311       if (joinedSession)
312       {
313         vclient.finalizeClient();
314         Cache.log.info("Jalview has left the session.");
315       }
316       else
317       {
318         Cache.log
319                 .warn("JV Client leaving a session that's its not joined yet.");
320       }
321       joinedSession = false;
322       vclient = null;
323       app = null;
324       user = null;
325       jv2vobj = null;
326       vobj2jv = null;
327     } catch (Exception e)
328     {
329       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
330     }
331   }
332
333   public void updateJalview(IClientDocument cdoc)
334   {
335     Cache.log.debug("Jalview updating from sesion document ..");
336     ensureJvVamsas();
337     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
338             baseProvEntry(), alRedoState);
339     vds.updateToJalview();
340     Cache.log.debug(".. finished updating from sesion document.");
341
342   }
343
344   private void ensureJvVamsas()
345   {
346     if (jv2vobj == null)
347     {
348       jv2vobj = new IdentityHashMap();
349       vobj2jv = new Hashtable();
350       alRedoState = new Hashtable();
351     }
352   }
353
354   /**
355    * jalview object binding to VorbaIds
356    */
357   IdentityHashMap jv2vobj = null;
358
359   Hashtable vobj2jv = null;
360   Hashtable alRedoState = null;
361   public void updateVamsasDocument(IClientDocument doc)
362   {
363     ensureJvVamsas();
364     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
365             baseProvEntry(), alRedoState);
366     // wander through frames
367     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
368
369     if (frames == null)
370     {
371       return;
372     }
373
374     try
375     {
376       // REVERSE ORDER
377       for (int i = frames.length - 1; i > -1; i--)
378       {
379         if (frames[i] instanceof AlignFrame)
380         {
381           AlignFrame af = (AlignFrame) frames[i];
382
383           // update alignment and root from frame.
384           vds.storeVAMSAS(af.getViewport(), af.getTitle());
385         }
386       }
387       // REVERSE ORDER
388       for (int i = frames.length - 1; i > -1; i--)
389       {
390         if (frames[i] instanceof AlignFrame)
391         {
392           AlignFrame af = (AlignFrame) frames[i];
393
394           // add any AlignedCodonFrame mappings on this alignment to any other.
395           vds.storeSequenceMappings(af.getViewport(), af.getTitle());
396         }
397       }
398     } catch (Exception e)
399     {
400       Cache.log.error("Vamsas Document store exception", e);
401     }
402   }
403
404   private Entry baseProvEntry()
405   {
406     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
407     pentry.setUser(user.getFullName());
408     pentry.setApp(app.getClientUrn());
409     pentry.setDate(new java.util.Date());
410     pentry.setAction("created");
411     return pentry;
412   }
413
414   /**
415    * do a vamsas document update or update jalview from the vamsas document
416    * 
417    * @param fromJalview
418    *                true to update from jalview to the vamsas document
419    */
420   protected void dealWithDocumentUpdate(boolean fromJalview)
421   {
422     // called by update handler for document update.
423     Cache.log.debug("Updating jalview from changed vamsas document.");
424     disableGui(true);
425     try
426     {
427       long time = System.currentTimeMillis();
428       IClientDocument cdoc = vclient.getClientDocument();
429       if (Cache.log.isDebugEnabled())
430       {
431         Cache.log.debug("Time taken to get ClientDocument = "
432                 + (System.currentTimeMillis() - time));
433         time = System.currentTimeMillis();
434       }
435       if (fromJalview)
436       {
437         this.updateVamsasDocument(cdoc);
438         if (Cache.log.isDebugEnabled())
439         {
440           Cache.log
441                   .debug("Time taken to update Vamsas Document from jalview\t= "
442                           + (System.currentTimeMillis() - time));
443           time = System.currentTimeMillis();
444         }
445         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
446         if (Cache.log.isDebugEnabled())
447         {
448           Cache.log.debug("Time taken to set Document Roots\t\t= "
449                   + (System.currentTimeMillis() - time));
450           time = System.currentTimeMillis();
451         }
452       }
453       else
454       {
455         updateJalview(cdoc);
456         if (Cache.log.isDebugEnabled())
457         {
458           Cache.log
459                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
460                           + (System.currentTimeMillis() - time));
461           time = System.currentTimeMillis();
462         }
463
464       }
465       vclient.updateDocument(cdoc);
466       if (Cache.log.isDebugEnabled())
467       {
468         Cache.log.debug("Time taken to update Session Document\t= "
469                 + (System.currentTimeMillis() - time));
470         time = System.currentTimeMillis();
471       }
472       cdoc = null;
473     } catch (Exception ee)
474     {
475       System.err.println("Exception whilst updating :");
476       ee.printStackTrace(System.err);
477     }
478     Cache.log.debug("Finished updating from document change.");
479     disableGui(false);
480   }
481
482   private void addDocumentUpdateHandler()
483   {
484     final VamsasApplication client = this;
485     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
486     {
487       public void propertyChange(PropertyChangeEvent evt)
488       {
489         Cache.log.debug("Dealing with document update event.");
490         client.dealWithDocumentUpdate(false);
491         Cache.log.debug("finished dealing with event.");
492       }
493     });
494     Cache.log.debug("Added Jalview handler for vamsas document updates.");
495   }
496
497   private void addStoreDocumentHandler()
498   {
499     final VamsasApplication client = this;
500     vclient.addVorbaEventHandler(uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE, new PropertyChangeListener()
501     {
502       public void propertyChange(PropertyChangeEvent evt)
503       {
504         Cache.log.debug("Asking user if the vamsas session should be stored.");
505         int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
506                 "The current VAMSAS session has unsaved data - do you want to save it ?",
507             "VAMSAS Session Shutdown",
508             JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
509         
510             if(reply==JOptionPane.YES_OPTION)
511             {
512               Cache.log.debug("Prompting for vamsas store filename.");
513               Desktop.instance.vamsasSave_actionPerformed(null);
514               Cache.log.debug("Finished attempt at storing document.");
515             } 
516             Cache.log.debug("finished dealing with REQUESTTOCLOSE event.");
517       }
518     });
519     Cache.log.debug("Added Jalview handler for vamsas document updates.");
520   }
521   public void disableGui(boolean b)
522   {
523     Desktop.instance.setVamsasUpdate(b);
524   }
525
526   private boolean joinedSession = false;
527
528   private VamsasListener picker = null;
529
530   private void startSession()
531   {
532     if (inSession())
533     {
534       try
535       {
536         vclient.joinSession();
537         joinedSession = true;
538       } catch (Exception e)
539       {
540         // Complain to GUI
541         Cache.log.error("Failed to join vamsas session.", e);
542         vclient = null;
543       }
544       try
545       {
546         final IPickManager pm = vclient.getPickManager();
547         final StructureSelectionManager ssm = StructureSelectionManager
548                 .getStructureSelectionManager();
549         pm.registerMessageHandler(new IMessageHandler()
550         {
551           String last = null;
552
553           public void handleMessage(Message message)
554           {
555             if (message instanceof MouseOverMessage && vobj2jv != null)
556             {
557               MouseOverMessage mm = (MouseOverMessage) message;
558               String mstring = mm.getVorbaID() + " " + mm.getPosition();
559               if (last != null && mstring.equals(last))
560               {
561                 return;
562               }
563               // if (Cache.log.isDebugEnabled())
564               // {
565               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
566               // "+mm.getPosition());
567               // }
568               Object jvobj = vobj2jv.get(mm.getVorbaID());
569               if (jvobj != null && jvobj instanceof SequenceI)
570               {
571                 last = mstring;
572                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
573                 // bound to "+jvobj+" at "+mm.getPosition());
574                 // position is character position in aligned sequence
575                 ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
576                         .getPosition());
577               }
578             }
579           }
580         });
581         picker = new VamsasListener()
582         {
583           SequenceI last = null;
584
585           int i = -1;
586
587           public void mouseOver(SequenceI seq, int index)
588           {
589             if (jv2vobj == null)
590               return;
591             if (seq != last || i != index)
592             {
593               VorbaId v = (VorbaId) jv2vobj.get(seq);
594               if (v != null)
595               {
596                 Cache.log.debug("Mouse over " + v.getId() + " bound to "
597                         + seq + " at " + index);
598                 last = seq;
599                 i = index;
600                 MouseOverMessage message = new MouseOverMessage(v.getId(),
601                         index);
602                 pm.sendMessage(message);
603               }
604             }
605           }
606         };
607         ssm.addStructureViewerListener(picker); // better method here
608       } catch (Exception e)
609       {
610         Cache.log.error("Failed to init Vamsas Picking", e);
611       }
612     }
613   }
614 }