Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / gui / VamsasApplication.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.SequenceGroup;
27 import jalview.datamodel.SequenceI;
28 import jalview.io.VamsasAppDatastore;
29 import jalview.structure.SelectionListener;
30 import jalview.structure.SelectionSource;
31 import jalview.structure.StructureSelectionManager;
32 import jalview.structure.VamsasListener;
33 import jalview.structure.VamsasSource;
34 import jalview.util.MessageManager;
35 import jalview.viewmodel.AlignmentViewport;
36
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.Hashtable;
42 import java.util.IdentityHashMap;
43 import java.util.Iterator;
44
45 import javax.swing.JInternalFrame;
46 import javax.swing.JOptionPane;
47
48 import uk.ac.vamsas.client.ClientHandle;
49 import uk.ac.vamsas.client.IClient;
50 import uk.ac.vamsas.client.IClientDocument;
51 import uk.ac.vamsas.client.InvalidSessionDocumentException;
52 import uk.ac.vamsas.client.UserHandle;
53 import uk.ac.vamsas.client.VorbaId;
54 import uk.ac.vamsas.client.picking.IMessageHandler;
55 import uk.ac.vamsas.client.picking.IPickManager;
56 import uk.ac.vamsas.client.picking.Message;
57 import uk.ac.vamsas.client.picking.MouseOverMessage;
58 import uk.ac.vamsas.client.picking.SelectionMessage;
59 import uk.ac.vamsas.objects.core.Entry;
60 import uk.ac.vamsas.objects.core.Input;
61 import uk.ac.vamsas.objects.core.Pos;
62 import uk.ac.vamsas.objects.core.Seg;
63
64 /**
65  * @author jimp
66  * 
67  */
68 public class VamsasApplication implements SelectionSource, VamsasSource
69 {
70   IClient vclient = null;
71
72   ClientHandle app = null;
73
74   UserHandle user = null;
75
76   Desktop jdesktop = null; // our jalview desktop reference
77
78   private boolean inInitialUpdate = true;
79
80   // Cache.preferences for vamsas client session arena
81   // preferences for check for default session at startup.
82   // user and organisation stuff.
83   public VamsasApplication(Desktop jdesktop, File sessionPath,
84           String sessionName)
85   {
86     // JBPNote:
87     // we should create a session URI from the sessionPath and pass it to
88     // the clientFactory - but the vamsas api doesn't cope with that yet.
89     this.jdesktop = jdesktop;
90     initClientSession(null, sessionPath, sessionName);
91   }
92
93   private static uk.ac.vamsas.client.IClientFactory getClientFactory()
94           throws IOException
95   {
96     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
97   }
98
99   /**
100    * Start a new vamsas session
101    * 
102    * @param jdesktop
103    */
104   public VamsasApplication(Desktop jdesktop)
105   {
106     this.jdesktop = jdesktop;
107     initClientSession(null, null);
108   }
109
110   /**
111    * init a connection to the session at the given url
112    * 
113    * @param jdesktop
114    * @param sessionUrl
115    */
116   public VamsasApplication(Desktop jdesktop, String sessionUrl)
117   {
118     this.jdesktop = jdesktop;
119     initClientSession(sessionUrl, null);
120   }
121
122   /**
123    * @throws IOException
124    *           or other if clientfactory instantiation failed.
125    * @return list of current sessions or null if no session exists.
126    */
127   public static String[] getSessionList() throws Exception
128   {
129     return getClientFactory().getCurrentSessions();
130   }
131
132   /**
133    * initialise, possibly with either a valid session url or a file for a new
134    * session
135    * 
136    * @param sess
137    *          null or a valid session url
138    * @param vamsasDocument
139    *          null or a valid vamsas document file
140    * @return false if no vamsas connection was made
141    */
142   private void initClientSession(String sess, File vamsasDocument)
143   {
144     initClientSession(sess, vamsasDocument, null);
145   }
146
147   private boolean initClientSession(String sess, File vamsasDocument,
148           String newDocSessionName)
149   {
150     try
151     {
152       // Only need to tell the library what the application is here
153       app = getJalviewHandle();
154       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
155       if (vamsasDocument != null)
156       {
157         if (sess != null)
158         {
159           throw new Error(
160                   MessageManager
161                           .getString("error.implementation_error_cannot_import_vamsas_doc"));
162         }
163         try
164         {
165           if (newDocSessionName != null)
166           {
167             vclient = clientfactory.openAsNewSessionIClient(app,
168                     vamsasDocument, newDocSessionName);
169           }
170           else
171           {
172             vclient = clientfactory.openAsNewSessionIClient(app,
173                     vamsasDocument);
174           }
175         } catch (InvalidSessionDocumentException e)
176         {
177           JOptionPane
178                   .showInternalMessageDialog(
179                           Desktop.desktop,
180
181                           MessageManager
182                                   .getString("label.vamsas_doc_couldnt_be_opened_as_new_session"),
183                           MessageManager
184                                   .getString("label.vamsas_document_import_failed"),
185                           JOptionPane.ERROR_MESSAGE);
186
187         }
188       }
189       else
190       {
191         // join existing or create a new session
192         if (sess == null)
193         {
194           vclient = clientfactory.getNewSessionIClient(app);
195         }
196         else
197         {
198           vclient = clientfactory.getIClient(app, sess);
199         }
200       }
201       // set some properties for our VAMSAS interaction
202       setVclientConfig();
203       user = vclient.getUserHandle();
204
205     } catch (Exception e)
206     {
207       jalview.bin.Cache.log
208               .error("Couldn't instantiate vamsas client !", e);
209       return false;
210     }
211     return true;
212   }
213
214   private void setVclientConfig()
215   {
216     if (vclient == null)
217     {
218       return;
219     }
220     try
221     {
222       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
223       {
224         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient) vclient)
225                 .getSimpleClientConfig();
226         cfg._validatemergedroots = false;
227         cfg._validateupdatedroots = true; // we may write rubbish otherwise.
228       }
229     } catch (Error e)
230     {
231       Cache.log
232               .warn("Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",
233                       e);
234     } catch (Exception e)
235     {
236       Cache.log
237               .warn("Probable VAMSAS client incompatibility - carrying on regardless",
238                       e);
239     }
240   }
241
242   /**
243    * make the appHandle for Jalview
244    * 
245    * @return
246    */
247   private ClientHandle getJalviewHandle()
248   {
249     return new ClientHandle("jalview.bin.Jalview",
250             jalview.bin.Cache.getProperty("VERSION"));
251   }
252
253   /**
254    * 
255    * @return true if we are registered in a vamsas session
256    */
257   public boolean inSession()
258   {
259     return (vclient != null);
260   }
261
262   /**
263    * called to connect to session inits handlers, does an initial document
264    * update.
265    */
266   public void initial_update()
267   {
268     if (!inSession())
269     {
270       throw new Error(
271               MessageManager
272                       .getString("error.implementation_error_vamsas_operation_not_init"));
273     }
274     addDocumentUpdateHandler();
275     addStoreDocumentHandler();
276     startSession();
277     inInitialUpdate = true;
278     Cache.log
279             .debug("Jalview loading the Vamsas Session for the first time.");
280     dealWithDocumentUpdate(false); // we don't push an update out to the
281     inInitialUpdate = false;
282     // document yet.
283     Cache.log.debug("... finished update for the first time.");
284   }
285
286   /**
287    * Update all windows after a vamsas datamodel change. this could go on the
288    * desktop object!
289    * 
290    */
291   protected void updateJalviewGui()
292   {
293     JInternalFrame[] frames = jdesktop.getAllFrames();
294
295     if (frames == null)
296     {
297       return;
298     }
299
300     try
301     {
302       // REVERSE ORDER
303       for (int i = frames.length - 1; i > -1; i--)
304       {
305         if (frames[i] instanceof AlignFrame)
306         {
307           AlignFrame af = (AlignFrame) frames[i];
308           af.alignPanel.alignmentChanged();
309         }
310       }
311     } catch (Exception e)
312     {
313       Cache.log
314               .warn("Exception whilst refreshing jalview windows after a vamsas document update.",
315                       e);
316     }
317   }
318
319   public void push_update()
320   {
321     Thread udthread = new Thread(new Runnable()
322     {
323
324       public void run()
325       {
326         Cache.log.info("Jalview updating to the Vamsas Session.");
327
328         dealWithDocumentUpdate(true);
329         Cache.log.info("Jalview finished updating to the Vamsas Session.");
330       }
331
332     });
333     udthread.start();
334   }
335
336   /**
337    * leave a session, prompting the user to save if necessary
338    */
339   public void end_session()
340   {
341     end_session(true);
342   }
343
344   private boolean promptUser = true;
345
346   /**
347    * leave a session, optionally prompting the user to save if necessary
348    * 
349    * @param promptUser
350    *          when true enable prompting by this application
351    */
352
353   public void end_session(boolean promptUser)
354   {
355     if (!inSession())
356     {
357       throw new Error(
358               MessageManager
359                       .getString("error.jalview_no_connected_vamsas_session"));
360     }
361     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
362     try
363     {
364       if (joinedSession)
365       {
366         boolean ourprompt = this.promptUser;
367         this.promptUser = promptUser;
368         vclient.finalizeClient();
369         Cache.log.info("Jalview has left the session.");
370         this.promptUser = ourprompt; // restore default value
371       }
372       else
373       {
374         Cache.log
375                 .warn("JV Client leaving a session that's its not joined yet.");
376       }
377       joinedSession = false;
378       vclient = null;
379       app = null;
380       user = null;
381       jv2vobj = null;
382       vobj2jv = null;
383     } catch (Exception e)
384     {
385       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
386     }
387   }
388
389   public void updateJalview(IClientDocument cdoc)
390   {
391     Cache.log.debug("Jalview updating from sesion document ..");
392     ensureJvVamsas();
393     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
394             baseProvEntry(), alRedoState);
395     try
396     {
397       vds.updateToJalview();
398     } catch (Exception e)
399     {
400       Cache.log.error("Failed to update Jalview from vamsas document.", e);
401     }
402     try
403     {
404       if (firstUpdate)
405       {
406         vds.updateJalviewFromAppdata();
407         // Comment this out to repeatedly read in data from JalviewAppData
408         // firstUpdate=false;
409       }
410     } catch (Exception e)
411     {
412       Cache.log.error(
413               "Exception when updating Jalview settings from Appdata.", e);
414     }
415     Cache.log.debug(".. finished updating from sesion document.");
416
417   }
418
419   boolean firstUpdate = false;
420
421   private void ensureJvVamsas()
422   {
423     if (jv2vobj == null)
424     {
425       jv2vobj = new IdentityHashMap();
426       vobj2jv = new Hashtable();
427       alRedoState = new Hashtable();
428       firstUpdate = true;
429     }
430   }
431
432   /**
433    * jalview object binding to VorbaIds
434    */
435   IdentityHashMap jv2vobj = null;
436
437   Hashtable vobj2jv = null;
438
439   Hashtable alRedoState = null;
440
441   boolean errorsDuringUpdate = false;
442
443   boolean errorsDuringAppUpdate = false;
444
445   /**
446    * update the document accessed through doc. A backup of the current object
447    * bindings is made.
448    * 
449    * @param doc
450    * @return number of views stored in document (updated and new views)
451    */
452   public int updateVamsasDocument(IClientDocument doc)
453   {
454     int storedviews = 0;
455     ensureJvVamsas();
456     errorsDuringUpdate = false;
457     errorsDuringAppUpdate = false;
458     backup_objectMapping();
459     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
460             baseProvEntry(), alRedoState);
461     // wander through frames
462     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
463
464     if (frames == null)
465     {
466       return 0;
467     }
468     Hashtable skipList = new Hashtable();
469     Hashtable viewset = new Hashtable();
470
471     try
472     {
473       // REVERSE ORDER
474       for (int i = frames.length - 1; i > -1; i--)
475       {
476         if (frames[i] instanceof AlignFrame)
477         {
478           AlignFrame af = (AlignFrame) frames[i];
479           if (!viewset.containsKey(af.getViewport().getSequenceSetId()))
480           {
481             // update alignment and root from frame.
482             boolean stored = false;
483             try
484             {
485               stored = vds.storeVAMSAS(af.getViewport(), af.getTitle());
486             } catch (Exception e)
487             {
488               errorsDuringUpdate = true;
489               Cache.log.error("Exception synchronizing "
490                       + af.getTitle()
491                       + " "
492                       + (af.getViewport().viewName == null ? "" : " view "
493                               + af.getViewport().viewName)
494                       + " to document.", e);
495               stored = false;
496             }
497             if (!stored)
498             { // record skip in skipList
499               skipList.put(af.getViewport().getSequenceSetId(), af);
500             }
501             else
502             {
503               storedviews++;
504               // could try to eliminate sequenceSetId from skiplist ..
505               // (skipList.containsKey(af.getViewport().getSequenceSetId()))
506               // remember sequenceSetId so we can skip all the other views on
507               // same alignment
508               viewset.put(af.getViewport().getSequenceSetId(), af);
509             }
510           }
511         }
512       }
513       // REVERSE ORDER
514       // for (int i = frames.length - 1; i > -1; i--)
515       // {
516       // if (frames[i] instanceof AlignFrame)
517       // {
518       // AlignFrame af = (AlignFrame) frames[i];
519       Iterator aframes = viewset.values().iterator();
520       while (aframes.hasNext())
521       {
522         AlignFrame af = (AlignFrame) aframes.next();
523         // add any AlignedCodonFrame mappings on this alignment to any other.
524         vds.storeSequenceMappings(af.getViewport(), af.getTitle());
525       }
526     } catch (Exception e)
527     {
528       Cache.log.error("Exception synchronizing Views to Document :", e);
529       errorsDuringUpdate = true;
530     }
531
532     try
533     {
534       if (viewset.size() > 0)
535       {
536         // Alignment views were synchronized, so store their state in the
537         // appData, too.
538         // The skipList ensures we don't write out any alignments not actually
539         // in the document.
540         vds.setSkipList(skipList);
541         vds.updateJalviewClientAppdata();
542       }
543     } catch (Exception e)
544     {
545       Cache.log.error("Client Appdata Write exception", e);
546       errorsDuringAppUpdate = true;
547     }
548     vds.clearSkipList();
549     return storedviews;
550   }
551
552   private Entry baseProvEntry()
553   {
554     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
555     pentry.setUser(user.getFullName());
556     pentry.setApp(app.getClientUrn());
557     pentry.setDate(new java.util.Date());
558     pentry.setAction("created");
559     return pentry;
560   }
561
562   /**
563    * do a vamsas document update or update jalview from the vamsas document
564    * 
565    * @param fromJalview
566    *          true to update from jalview to the vamsas document
567    * @return total number of stored alignments in the document after the update
568    */
569   protected int dealWithDocumentUpdate(boolean fromJalview)
570   {
571     int storedviews = 0;
572     // called by update handler for document update.
573     Cache.log.debug("Updating jalview from changed vamsas document.");
574     disableGui(true);
575     try
576     {
577       long time = System.currentTimeMillis();
578       IClientDocument cdoc = vclient.getClientDocument();
579       if (Cache.log.isDebugEnabled())
580       {
581         Cache.log.debug("Time taken to get ClientDocument = "
582                 + (System.currentTimeMillis() - time));
583         time = System.currentTimeMillis();
584       }
585       if (fromJalview)
586       {
587         storedviews += updateVamsasDocument(cdoc);
588         if (Cache.log.isDebugEnabled())
589         {
590           Cache.log
591                   .debug("Time taken to update Vamsas Document from jalview\t= "
592                           + (System.currentTimeMillis() - time));
593           time = System.currentTimeMillis();
594         }
595         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
596         if (Cache.log.isDebugEnabled())
597         {
598           Cache.log.debug("Time taken to set Document Roots\t\t= "
599                   + (System.currentTimeMillis() - time));
600           time = System.currentTimeMillis();
601         }
602       }
603       else
604       {
605         updateJalview(cdoc);
606         if (Cache.log.isDebugEnabled())
607         {
608           Cache.log
609                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
610                           + (System.currentTimeMillis() - time));
611           time = System.currentTimeMillis();
612         }
613
614       }
615       vclient.updateDocument(cdoc);
616       if (Cache.log.isDebugEnabled())
617       {
618         Cache.log.debug("Time taken to update Session Document\t= "
619                 + (System.currentTimeMillis() - time));
620         time = System.currentTimeMillis();
621       }
622       cdoc = null;
623     } catch (Exception ee)
624     {
625       System.err.println("Exception whilst updating :");
626       ee.printStackTrace(System.err);
627       // recover object map backup, since its probably corrupted with references
628       // to Vobjects that don't exist anymore.
629       recover_objectMappingBackup();
630       storedviews = 0;
631     }
632     Cache.log.debug("Finished updating from document change.");
633     disableGui(false);
634     return storedviews;
635   }
636
637   private void addDocumentUpdateHandler()
638   {
639     final VamsasApplication client = this;
640     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
641     {
642       public void propertyChange(PropertyChangeEvent evt)
643       {
644         Cache.log.debug("Dealing with document update event.");
645         client.dealWithDocumentUpdate(false);
646         Cache.log.debug("finished dealing with event.");
647       }
648     });
649     Cache.log.debug("Added Jalview handler for vamsas document updates.");
650   }
651
652   private void addStoreDocumentHandler()
653   {
654     final VamsasApplication client = this;
655     vclient.addVorbaEventHandler(
656             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
657             new PropertyChangeListener()
658             {
659               public void propertyChange(PropertyChangeEvent evt)
660               {
661                 if (client.promptUser)
662                 {
663                   Cache.log
664                           .debug("Asking user if the vamsas session should be stored.");
665                   int reply = JOptionPane
666                           .showInternalConfirmDialog(
667                                   Desktop.desktop,
668                                   "The current VAMSAS session has unsaved data - do you want to save it ?",
669                                   "VAMSAS Session Shutdown",
670                                   JOptionPane.YES_NO_OPTION,
671                                   JOptionPane.QUESTION_MESSAGE);
672
673                   if (reply == JOptionPane.YES_OPTION)
674                   {
675                     Cache.log.debug("Prompting for vamsas store filename.");
676                     Desktop.instance.vamsasSave_actionPerformed(null);
677                     Cache.log
678                             .debug("Finished attempt at storing document.");
679                   }
680                   Cache.log
681                           .debug("finished dealing with REQUESTTOCLOSE event.");
682                 }
683                 else
684                 {
685                   Cache.log
686                           .debug("Ignoring store document request (promptUser==false)");
687                 }
688               }
689             });
690     Cache.log.debug("Added Jalview handler for vamsas document updates.");
691   }
692
693   public void disableGui(boolean b)
694   {
695     Desktop.instance.setVamsasUpdate(b);
696   }
697
698   Hashtable _backup_vobj2jv;
699
700   IdentityHashMap _backup_jv2vobj;
701
702   /**
703    * make a backup of the object mappings (vobj2jv and jv2vobj)
704    */
705   public void backup_objectMapping()
706   {
707     _backup_vobj2jv = new Hashtable(vobj2jv);
708     _backup_jv2vobj = new IdentityHashMap(jv2vobj);
709   }
710
711   /**
712    * recover original object mappings from the object mapping backup if document
713    * IO failed
714    * 
715    * @throws Error
716    *           if backup_objectMapping was not called.
717    */
718   public void recover_objectMappingBackup()
719   {
720     if (_backup_vobj2jv == null)
721     {
722       if (inInitialUpdate)
723       {
724         // nothing to recover so just
725         return;
726       }
727
728       throw new Error(
729               MessageManager
730                       .getString("error.implementation_error_cannot_recover_vamsas_object_mappings"));
731     }
732     jv2vobj.clear();
733     Iterator el = _backup_jv2vobj.entrySet().iterator();
734     while (el.hasNext())
735     {
736       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
737       jv2vobj.put(mp.getKey(), mp.getValue());
738     }
739     el = _backup_vobj2jv.entrySet().iterator();
740     while (el.hasNext())
741     {
742       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
743       vobj2jv.put(mp.getKey(), mp.getValue());
744     }
745   }
746
747   private boolean joinedSession = false;
748
749   private VamsasListener picker = null;
750
751   private SelectionListener selecter;
752
753   private void startSession()
754   {
755     if (inSession())
756     {
757       try
758       {
759         vclient.joinSession();
760         joinedSession = true;
761       } catch (Exception e)
762       {
763         // Complain to GUI
764         Cache.log.error("Failed to join vamsas session.", e);
765         vclient = null;
766       }
767       try
768       {
769         final IPickManager pm = vclient.getPickManager();
770         final StructureSelectionManager ssm = StructureSelectionManager
771                 .getStructureSelectionManager(Desktop.instance);
772         final VamsasApplication me = this;
773         pm.registerMessageHandler(new IMessageHandler()
774         {
775           String last = null;
776
777           public void handleMessage(Message message)
778           {
779             if (vobj2jv == null)
780             {
781               // we are not in a session yet.
782               return;
783             }
784             if (message instanceof MouseOverMessage)
785             {
786               MouseOverMessage mm = (MouseOverMessage) message;
787               String mstring = mm.getVorbaID() + " " + mm.getPosition();
788               if (last != null && mstring.equals(last))
789               {
790                 return;
791               }
792               // if (Cache.log.isDebugEnabled())
793               // {
794               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
795               // "+mm.getPosition());
796               // }
797               Object jvobj = vobj2jv.get(mm.getVorbaID());
798               if (jvobj != null && jvobj instanceof SequenceI)
799               {
800                 last = mstring;
801                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
802                 // bound to "+jvobj+" at "+mm.getPosition());
803                 // position is character position in aligned sequence
804                 ssm.mouseOverVamsasSequence((SequenceI) jvobj,
805                         mm.getPosition(), me);
806               }
807             }
808             if (message instanceof uk.ac.vamsas.client.picking.SelectionMessage)
809             {
810               // we only care about AlignmentSequence selections
811               SelectionMessage sm = (SelectionMessage) message;
812               sm.validate();
813               System.err.println("Received\n" + sm.getRawMessage());
814               Object[] jvobjs = sm.getVorbaIDs() == null ? null
815                       : new Object[sm.getVorbaIDs().length];
816               if (jvobjs == null)
817               {
818                 // TODO: rationalise : can only clear a selection over a
819                 // referred to object
820                 ssm.sendSelection(null, null, me);
821                 return;
822               }
823               Class type = null;
824               boolean send = true;
825               for (int o = 0; o < jvobjs.length; o++)
826               {
827                 jvobjs[o] = vobj2jv.get(sm.getVorbaIDs()[o]);
828                 if (jvobjs[o] == null)
829                 {
830                   // can't cope with selections for unmapped objects
831                   continue;
832                 }
833                 if (type == null)
834                 {
835                   type = jvobjs[o].getClass();
836                 }
837                 ;
838                 if (type != jvobjs[o].getClass())
839                 {
840                   send = false;
841                   // discard - can't cope with selections over mixed objects
842                   // continue;
843                 }
844               }
845               SequenceGroup jselection = null;
846               ColumnSelection colsel = null;
847               if (type == jalview.datamodel.Alignment.class)
848               {
849                 if (jvobjs.length == 1)
850                 {
851                   // TODO if (sm.isNone())// send a message to select the
852                   // specified columns over the
853                   // given
854                   // alignment
855
856                   send = true;
857                 }
858               }
859               if (type == jalview.datamodel.Sequence.class)
860               {
861
862                 SequenceI seq;
863                 boolean aligned = ((jalview.datamodel.Sequence) jvobjs[0])
864                         .getDatasetSequence() != null;
865                 int maxWidth = 0;
866                 if (aligned)
867                 {
868                   jselection = new SequenceGroup();
869                   jselection.addSequence(
870                           seq = (jalview.datamodel.Sequence) jvobjs[0],
871                           false);
872                   maxWidth = seq.getLength();
873                 }
874                 for (int c = 1; aligned && jvobjs.length > 1
875                         && c < jvobjs.length; c++)
876                 {
877                   if (((jalview.datamodel.Sequence) jvobjs[c])
878                           .getDatasetSequence() == null)
879                   {
880                     aligned = false;
881                     continue;
882                   }
883                   else
884                   {
885                     jselection.addSequence(
886                             seq = (jalview.datamodel.Sequence) jvobjs[c],
887                             false);
888                     if (maxWidth < seq.getLength())
889                     {
890                       maxWidth = seq.getLength();
891                     }
892
893                   }
894                 }
895                 if (!aligned)
896                 {
897                   jselection = null;
898                   // if cardinality is greater than one then verify all
899                   // sequences are alignment sequences.
900                   if (jvobjs.length == 1)
901                   {
902                     // find all instances of this dataset sequence in the
903                     // displayed alignments containing the associated range and
904                     // select them.
905                   }
906                 }
907                 else
908                 {
909                   jselection.setStartRes(0);
910                   jselection.setEndRes(maxWidth);
911                   // locate the alignment containing the given sequences and
912                   // select the associated ranges on them.
913                   if (sm.getRanges() != null)
914                   {
915                     int[] prange = uk.ac.vamsas.objects.utils.Range
916                             .getBounds(sm.getRanges());
917                     jselection.setStartRes(prange[0] - 1);
918                     jselection.setEndRes(prange[1] - 1);
919                     prange = uk.ac.vamsas.objects.utils.Range
920                             .getIntervals(sm.getRanges());
921                     colsel = new ColumnSelection();
922                     for (int p = 0; p < prange.length; p += 2)
923                     {
924                       int d = (prange[p] <= prange[p + 1]) ? 1 : -1;
925                       // try to join up adjacent columns to make a larger
926                       // selection
927                       // lower and upper bounds
928                       int l = (d < 0) ? 1 : 0;
929                       int u = (d > 0) ? 1 : 0;
930
931                       if (jselection.getStartRes() > 0
932                               && prange[p + l] == jselection.getStartRes())
933                       {
934                         jselection.setStartRes(prange[p + l] - 1);
935                       }
936                       if (jselection.getEndRes() <= maxWidth
937                               && prange[p + u] == (jselection.getEndRes() + 2))
938                       {
939                         jselection.setEndRes(prange[p + u] - 1);
940                       }
941                       // mark all the columns in the range.
942                       for (int sr = prange[p], er = prange[p + 1], de = er
943                               + d; sr != de; sr += d)
944                       {
945                         colsel.addElement(sr - 1);
946                       }
947                     }
948                   }
949                   send = true;
950                 }
951               }
952               if (send)
953               {
954                 ssm.sendSelection(jselection, colsel, me);
955               }
956               // discard message.
957               for (int c = 0; c < jvobjs.length; c++)
958               {
959                 jvobjs[c] = null;
960               }
961               ;
962               jvobjs = null;
963               return;
964             }
965           }
966         });
967         picker = new VamsasListener()
968         {
969           SequenceI last = null;
970
971           int i = -1;
972
973           @Override
974           public void mouseOverSequence(SequenceI seq, int index,
975                   VamsasSource source)
976           {
977             if (jv2vobj == null)
978             {
979               return;
980             }
981             if (seq != last || i != index)
982             {
983               VorbaId v = (VorbaId) jv2vobj.get(seq);
984               if (v != null)
985               {
986                 // this should really be a trace message.
987                 // Cache.log.debug("Mouse over " + v.getId() + " bound to "
988                 // + seq + " at " + index);
989                 last = seq;
990                 i = index;
991                 MouseOverMessage message = new MouseOverMessage(v.getId(),
992                         index);
993                 pm.sendMessage(message);
994               }
995             }
996           }
997         };
998         selecter = new SelectionListener()
999         {
1000
1001           public void selection(SequenceGroup seqsel,
1002                   ColumnSelection colsel, SelectionSource source)
1003           {
1004             if (vobj2jv == null)
1005             {
1006               Cache.log
1007                       .warn("Selection listener still active for dead session.");
1008               // not in a session.
1009               return;
1010             }
1011             if (source != me)
1012             {
1013               AlignmentI visal = null;
1014               if (source instanceof AlignViewport)
1015               {
1016                 visal = ((AlignmentViewport) source).getAlignment();
1017               }
1018               SelectionMessage sm = null;
1019               if ((seqsel == null || seqsel.getSize() == 0)
1020                       && (colsel == null || colsel.getSelected() == null || colsel
1021                               .getSelected().size() == 0))
1022               {
1023                 if (source instanceof AlignViewport)
1024                 {
1025                   // the empty selection.
1026                   sm = new SelectionMessage("jalview",
1027                           new String[] { ((AlignmentViewport) source)
1028                                   .getSequenceSetId() }, null, true);
1029                 }
1030                 else
1031                 {
1032                   // the empty selection.
1033                   sm = new SelectionMessage("jalview", null, null, true);
1034                 }
1035               }
1036               else
1037               {
1038                 String[] vobj = new String[seqsel.getSize()];
1039                 int o = 0;
1040                 for (SequenceI sel : seqsel.getSequences(null))
1041                 {
1042                   VorbaId v = (VorbaId) jv2vobj.get(sel);
1043                   if (v != null)
1044                   {
1045                     vobj[o++] = v.toString();
1046                   }
1047                 }
1048                 if (o < vobj.length)
1049                 {
1050                   String t[] = vobj;
1051                   vobj = new String[o];
1052                   System.arraycopy(t, 0, vobj, 0, o);
1053                   t = null;
1054                 }
1055                 Input range = null;
1056                 if (seqsel != null && colsel != null)
1057                 {
1058                   // deparse the colsel into positions on the vamsas alignment
1059                   // sequences
1060                   range = new Input();
1061                   if (colsel.getSelected() != null
1062                           && colsel.getSelected().size() > 0
1063                           && visal != null
1064                           && seqsel.getSize() == visal.getHeight())
1065                   {
1066                     // gather selected columns outwith the sequence positions
1067                     // too
1068                     for (Object obj : colsel.getSelected())
1069                     {
1070                       int ival = ((Integer) obj).intValue();
1071                       Pos p = new Pos();
1072                       p.setI(ival + 1);
1073                       range.addPos(p);
1074                     }
1075                   }
1076                   else
1077                   {
1078                     int[] intervals = colsel.getVisibleContigs(
1079                             seqsel.getStartRes(), seqsel.getEndRes() + 1);
1080                     for (int iv = 0; iv < intervals.length; iv += 2)
1081                     {
1082                       Seg s = new Seg();
1083                       s.setStart(intervals[iv] + 1); // vamsas indices begin at
1084                       // 1, not zero.
1085                       s.setEnd(intervals[iv + 1] + 1);
1086                       s.setInclusive(true);
1087                       range.addSeg(s);
1088                     }
1089                   }
1090                 }
1091                 if (vobj.length > 0)
1092                 {
1093                   sm = new SelectionMessage("jalview", vobj, range);
1094                 }
1095                 else
1096                 {
1097                   sm = null;
1098                 }
1099               }
1100               if (sm != null)
1101               {
1102                 sm.validate(); // debug
1103                 Cache.log.debug("Selection Message\n" + sm.getRawMessage());
1104                 pm.sendMessage(sm);
1105               }
1106             }
1107           }
1108
1109         };
1110         ssm.addStructureViewerListener(picker); // better method here
1111         ssm.addSelectionListener(selecter);
1112       } catch (Exception e)
1113       {
1114         Cache.log.error("Failed to init Vamsas Picking", e);
1115       }
1116     }
1117   }
1118
1119   public String getCurrentSession()
1120   {
1121     if (vclient != null)
1122     {
1123       return (vclient.getSessionUrn());
1124     }
1125     return null;
1126   }
1127 }