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