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