973cfe85cccae502f1749a70d6825d6ca6bd7743
[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(MessageManager.getString(
268               "error.implementation_error_vamsas_operation_not_init"));
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(MessageManager
355               .getString("error.jalview_no_connected_vamsas_session"));
356     }
357     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
358     try
359     {
360       if (joinedSession)
361       {
362         boolean ourprompt = this.promptUser;
363         this.promptUser = promptUser;
364         vclient.finalizeClient();
365         Cache.log.info("Jalview has left the session.");
366         this.promptUser = ourprompt; // restore default value
367       }
368       else
369       {
370         Cache.log.warn(
371                 "JV Client leaving a session that's its not joined yet.");
372       }
373       joinedSession = false;
374       vclient = null;
375       app = null;
376       user = null;
377       jv2vobj = null;
378       vobj2jv = null;
379     } catch (Exception e)
380     {
381       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
382     }
383   }
384
385   public void updateJalview(IClientDocument cdoc)
386   {
387     Cache.log.debug("Jalview updating from sesion document ..");
388     ensureJvVamsas();
389     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
390             baseProvEntry(), alRedoState);
391     try
392     {
393       vds.updateToJalview();
394     } catch (Exception e)
395     {
396       Cache.log.error("Failed to update Jalview from vamsas document.", e);
397     }
398     try
399     {
400       if (firstUpdate)
401       {
402         vds.updateJalviewFromAppdata();
403         // Comment this out to repeatedly read in data from JalviewAppData
404         // firstUpdate=false;
405       }
406     } catch (Exception e)
407     {
408       Cache.log.error(
409               "Exception when updating Jalview settings from Appdata.", e);
410     }
411     Cache.log.debug(".. finished updating from sesion document.");
412
413   }
414
415   boolean firstUpdate = false;
416
417   private void ensureJvVamsas()
418   {
419     if (jv2vobj == null)
420     {
421       jv2vobj = new IdentityHashMap();
422       vobj2jv = new Hashtable();
423       alRedoState = new Hashtable();
424       firstUpdate = true;
425     }
426   }
427
428   /**
429    * jalview object binding to VorbaIds
430    */
431   IdentityHashMap jv2vobj = null;
432
433   Hashtable vobj2jv = null;
434
435   Hashtable alRedoState = null;
436
437   boolean errorsDuringUpdate = false;
438
439   boolean errorsDuringAppUpdate = false;
440
441   /**
442    * update the document accessed through doc. A backup of the current object
443    * bindings is made.
444    * 
445    * @param doc
446    * @return number of views stored in document (updated and new views)
447    */
448   public int updateVamsasDocument(IClientDocument doc)
449   {
450     int storedviews = 0;
451     ensureJvVamsas();
452     errorsDuringUpdate = false;
453     errorsDuringAppUpdate = false;
454     backup_objectMapping();
455     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
456             baseProvEntry(), alRedoState);
457     // wander through frames
458     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
459
460     if (frames == null)
461     {
462       return 0;
463     }
464     Hashtable skipList = new Hashtable();
465     Hashtable viewset = new Hashtable();
466
467     try
468     {
469       // REVERSE ORDER
470       for (int i = frames.length - 1; i > -1; i--)
471       {
472         if (frames[i] instanceof AlignFrame)
473         {
474           AlignFrame af = (AlignFrame) frames[i];
475           if (!viewset.containsKey(af.getViewport().getSequenceSetId()))
476           {
477             // update alignment and root from frame.
478             boolean stored = false;
479             try
480             {
481               stored = vds.storeVAMSAS(af.getViewport(), af.getTitle());
482             } catch (Exception e)
483             {
484               errorsDuringUpdate = true;
485               Cache.log.error("Exception synchronizing " + af.getTitle()
486                       + " "
487                       + (af.getViewport().viewName == null ? ""
488                               : " view " + af.getViewport().viewName)
489                       + " to document.", e);
490               stored = false;
491             }
492             if (!stored)
493             { // record skip in skipList
494               skipList.put(af.getViewport().getSequenceSetId(), af);
495             }
496             else
497             {
498               storedviews++;
499               // could try to eliminate sequenceSetId from skiplist ..
500               // (skipList.containsKey(af.getViewport().getSequenceSetId()))
501               // remember sequenceSetId so we can skip all the other views on
502               // same alignment
503               viewset.put(af.getViewport().getSequenceSetId(), af);
504             }
505           }
506         }
507       }
508       // REVERSE ORDER
509       // for (int i = frames.length - 1; i > -1; i--)
510       // {
511       // if (frames[i] instanceof AlignFrame)
512       // {
513       // AlignFrame af = (AlignFrame) frames[i];
514       Iterator aframes = viewset.values().iterator();
515       while (aframes.hasNext())
516       {
517         AlignFrame af = (AlignFrame) aframes.next();
518         // add any AlignedCodonFrame mappings on this alignment to any other.
519         vds.storeSequenceMappings(af.getViewport(), af.getTitle());
520       }
521     } catch (Exception e)
522     {
523       Cache.log.error("Exception synchronizing Views to Document :", e);
524       errorsDuringUpdate = true;
525     }
526
527     try
528     {
529       if (viewset.size() > 0)
530       {
531         // Alignment views were synchronized, so store their state in the
532         // appData, too.
533         // The skipList ensures we don't write out any alignments not actually
534         // in the document.
535         vds.setSkipList(skipList);
536         vds.updateJalviewClientAppdata();
537       }
538     } catch (Exception e)
539     {
540       Cache.log.error("Client Appdata Write exception", e);
541       errorsDuringAppUpdate = true;
542     }
543     vds.clearSkipList();
544     return storedviews;
545   }
546
547   private Entry baseProvEntry()
548   {
549     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
550     pentry.setUser(user.getFullName());
551     pentry.setApp(app.getClientUrn());
552     pentry.setDate(new java.util.Date());
553     pentry.setAction("created");
554     return pentry;
555   }
556
557   /**
558    * do a vamsas document update or update jalview from the vamsas document
559    * 
560    * @param fromJalview
561    *          true to update from jalview to the vamsas document
562    * @return total number of stored alignments in the document after the update
563    */
564   protected int dealWithDocumentUpdate(boolean fromJalview)
565   {
566     int storedviews = 0;
567     // called by update handler for document update.
568     Cache.log.debug("Updating jalview from changed vamsas document.");
569     disableGui(true);
570     try
571     {
572       long time = System.currentTimeMillis();
573       IClientDocument cdoc = vclient.getClientDocument();
574       if (Cache.log.isDebugEnabled())
575       {
576         Cache.log.debug("Time taken to get ClientDocument = "
577                 + (System.currentTimeMillis() - time));
578         time = System.currentTimeMillis();
579       }
580       if (fromJalview)
581       {
582         storedviews += updateVamsasDocument(cdoc);
583         if (Cache.log.isDebugEnabled())
584         {
585           Cache.log.debug(
586                   "Time taken to update Vamsas Document from jalview\t= "
587                           + (System.currentTimeMillis() - time));
588           time = System.currentTimeMillis();
589         }
590         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
591         if (Cache.log.isDebugEnabled())
592         {
593           Cache.log.debug("Time taken to set Document Roots\t\t= "
594                   + (System.currentTimeMillis() - time));
595           time = System.currentTimeMillis();
596         }
597       }
598       else
599       {
600         updateJalview(cdoc);
601         if (Cache.log.isDebugEnabled())
602         {
603           Cache.log.debug(
604                   "Time taken to update Jalview from vamsas document Roots\t= "
605                           + (System.currentTimeMillis() - time));
606           time = System.currentTimeMillis();
607         }
608
609       }
610       vclient.updateDocument(cdoc);
611       if (Cache.log.isDebugEnabled())
612       {
613         Cache.log.debug("Time taken to update Session Document\t= "
614                 + (System.currentTimeMillis() - time));
615         time = System.currentTimeMillis();
616       }
617       cdoc = null;
618     } catch (Exception ee)
619     {
620       System.err.println("Exception whilst updating :");
621       ee.printStackTrace(System.err);
622       // recover object map backup, since its probably corrupted with references
623       // to Vobjects that don't exist anymore.
624       recover_objectMappingBackup();
625       storedviews = 0;
626     }
627     Cache.log.debug("Finished updating from document change.");
628     disableGui(false);
629     return storedviews;
630   }
631
632   private void addDocumentUpdateHandler()
633   {
634     final VamsasApplication client = this;
635     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
636     {
637       @Override
638       public void propertyChange(PropertyChangeEvent evt)
639       {
640         Cache.log.debug("Dealing with document update event.");
641         client.dealWithDocumentUpdate(false);
642         Cache.log.debug("finished dealing with event.");
643       }
644     });
645     Cache.log.debug("Added Jalview handler for vamsas document updates.");
646   }
647
648   private void addStoreDocumentHandler()
649   {
650     final VamsasApplication client = this;
651     vclient.addVorbaEventHandler(
652             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
653             new PropertyChangeListener()
654             {
655               @Override
656               public void propertyChange(PropertyChangeEvent evt)
657               {
658                 if (client.promptUser)
659                 {
660                   Cache.log.debug(
661                           "Asking user if the vamsas session should be stored.");
662                   int reply = JvOptionPane.showInternalConfirmDialog(
663                           Desktop.desktop,
664                           "The current VAMSAS session has unsaved data - do you want to save it ?",
665                           "VAMSAS Session Shutdown",
666                           JvOptionPane.YES_NO_OPTION,
667                           JvOptionPane.QUESTION_MESSAGE);
668
669                   if (reply == JvOptionPane.YES_OPTION)
670                   {
671                     Cache.log.debug("Prompting for vamsas store filename.");
672                     Desktop.instance.vamsasSave_actionPerformed(null);
673                     Cache.log
674                             .debug("Finished attempt at storing document.");
675                   }
676                   Cache.log.debug(
677                           "finished dealing with REQUESTTOCLOSE event.");
678                 }
679                 else
680                 {
681                   Cache.log.debug(
682                           "Ignoring store document request (promptUser==false)");
683                 }
684               }
685             });
686     Cache.log.debug("Added Jalview handler for vamsas document updates.");
687   }
688
689   public void disableGui(boolean b)
690   {
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(MessageManager.getString(
725               "error.implementation_error_cannot_recover_vamsas_object_mappings"));
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 }