added store of undo/redo hash for each alignment so local state changes can be detect...
[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(), alRedoState);
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       alRedoState = new Hashtable();
299     }
300   }
301
302   /**
303    * jalview object binding to VorbaIds
304    */
305   IdentityHashMap jv2vobj = null;
306
307   Hashtable vobj2jv = null;
308   Hashtable alRedoState = null;
309   public void updateVamsasDocument(IClientDocument doc)
310   {
311     ensureJvVamsas();
312     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
313             baseProvEntry(), alRedoState);
314     // wander through frames
315     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
316
317     if (frames == null)
318     {
319       return;
320     }
321
322     try
323     {
324       // REVERSE ORDER
325       for (int i = frames.length - 1; i > -1; i--)
326       {
327         if (frames[i] instanceof AlignFrame)
328         {
329           AlignFrame af = (AlignFrame) frames[i];
330
331           // update alignment and root from frame.
332           vds.storeVAMSAS(af.getViewport(), af.getTitle());
333         }
334       }
335       // REVERSE ORDER
336       for (int i = frames.length - 1; i > -1; i--)
337       {
338         if (frames[i] instanceof AlignFrame)
339         {
340           AlignFrame af = (AlignFrame) frames[i];
341
342           // add any AlignedCodonFrame mappings on this alignment to any other.
343           vds.storeSequenceMappings(af.getViewport(), af.getTitle());
344         }
345       }
346     } catch (Exception e)
347     {
348       Cache.log.error("Vamsas Document store exception", e);
349     }
350   }
351
352   private Entry baseProvEntry()
353   {
354     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
355     pentry.setUser(user.getFullName());
356     pentry.setApp(app.getClientUrn());
357     pentry.setDate(new java.util.Date());
358     pentry.setAction("created");
359     return pentry;
360   }
361
362   /**
363    * do a vamsas document update or update jalview from the vamsas document
364    * 
365    * @param fromJalview
366    *                true to update from jalview to the vamsas document
367    */
368   protected void dealWithDocumentUpdate(boolean fromJalview)
369   {
370     // called by update handler for document update.
371     Cache.log.debug("Updating jalview from changed vamsas document.");
372     disableGui(true);
373     try
374     {
375       long time = System.currentTimeMillis();
376       IClientDocument cdoc = vclient.getClientDocument();
377       if (Cache.log.isDebugEnabled())
378       {
379         Cache.log.debug("Time taken to get ClientDocument = "
380                 + (System.currentTimeMillis() - time));
381         time = System.currentTimeMillis();
382       }
383       if (fromJalview)
384       {
385         this.updateVamsasDocument(cdoc);
386         if (Cache.log.isDebugEnabled())
387         {
388           Cache.log
389                   .debug("Time taken to update Vamsas Document from jalview\t= "
390                           + (System.currentTimeMillis() - time));
391           time = System.currentTimeMillis();
392         }
393         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
394         if (Cache.log.isDebugEnabled())
395         {
396           Cache.log.debug("Time taken to set Document Roots\t\t= "
397                   + (System.currentTimeMillis() - time));
398           time = System.currentTimeMillis();
399         }
400       }
401       else
402       {
403         updateJalview(cdoc);
404         if (Cache.log.isDebugEnabled())
405         {
406           Cache.log
407                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
408                           + (System.currentTimeMillis() - time));
409           time = System.currentTimeMillis();
410         }
411
412       }
413       vclient.updateDocument(cdoc);
414       if (Cache.log.isDebugEnabled())
415       {
416         Cache.log.debug("Time taken to update Session Document\t= "
417                 + (System.currentTimeMillis() - time));
418         time = System.currentTimeMillis();
419       }
420       cdoc = null;
421     } catch (Exception ee)
422     {
423       System.err.println("Exception whilst updating :");
424       ee.printStackTrace(System.err);
425     }
426     Cache.log.debug("Finished updating from document change.");
427     disableGui(false);
428   }
429
430   private void addDocumentUpdateHandler()
431   {
432     final VamsasApplication client = this;
433     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
434     {
435       public void propertyChange(PropertyChangeEvent evt)
436       {
437         Cache.log.debug("Dealing with document update event.");
438         client.dealWithDocumentUpdate(false);
439         Cache.log.debug("finished dealing with event.");
440       }
441     });
442     Cache.log.debug("Added Jalview handler for vamsas document updates.");
443   }
444
445   public void disableGui(boolean b)
446   {
447     Desktop.instance.setVamsasUpdate(b);
448   }
449
450   private boolean joinedSession = false;
451
452   private VamsasListener picker = null;
453
454   private void startSession()
455   {
456     if (inSession())
457     {
458       try
459       {
460         vclient.joinSession();
461         joinedSession = true;
462       } catch (Exception e)
463       {
464         // Complain to GUI
465         Cache.log.error("Failed to join vamsas session.", e);
466         vclient = null;
467       }
468       try
469       {
470         final IPickManager pm = vclient.getPickManager();
471         final StructureSelectionManager ssm = StructureSelectionManager
472                 .getStructureSelectionManager();
473         pm.registerMessageHandler(new IMessageHandler()
474         {
475           String last = null;
476
477           public void handleMessage(Message message)
478           {
479             if (message instanceof MouseOverMessage && vobj2jv != null)
480             {
481               MouseOverMessage mm = (MouseOverMessage) message;
482               String mstring = mm.getVorbaID() + " " + mm.getPosition();
483               if (last != null && mstring.equals(last))
484               {
485                 return;
486               }
487               // if (Cache.log.isDebugEnabled())
488               // {
489               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
490               // "+mm.getPosition());
491               // }
492               Object jvobj = vobj2jv.get(mm.getVorbaID());
493               if (jvobj != null && jvobj instanceof SequenceI)
494               {
495                 last = mstring;
496                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
497                 // bound to "+jvobj+" at "+mm.getPosition());
498                 // position is in sequence or in aligned sequence ???????
499                 ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
500                         .getPosition());
501               }
502             }
503           }
504         });
505         picker = new VamsasListener()
506         {
507           SequenceI last = null;
508
509           int i = -1;
510
511           public void mouseOver(SequenceI seq, int index)
512           {
513             if (jv2vobj == null)
514               return;
515             if (seq != last || i != index)
516             {
517               VorbaId v = (VorbaId) jv2vobj.get(seq);
518               if (v != null)
519               {
520                 Cache.log.debug("Mouse over " + v.getId() + " bound to "
521                         + seq + " at " + index);
522                 last = seq;
523                 i = index;
524                 MouseOverMessage message = new MouseOverMessage(v.getId(),
525                         index);
526                 pm.sendMessage(message);
527               }
528             }
529           }
530         };
531         ssm.addStructureViewerListener(picker); // better method here
532       } catch (Exception e)
533       {
534         Cache.log.error("Failed to init Vamsas Picking", e);
535       }
536     }
537   }
538 }