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