Merge branch 'releases/Release_2_11_4_Branch'
[jalview.git] / 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(
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           JvOptionPane
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                           JvOptionPane.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       @Override
325       public void run()
326       {
327         Cache.log.info("Jalview updating to the Vamsas Session.");
328
329         dealWithDocumentUpdate(true);
330         Cache.log.info("Jalview finished updating to the Vamsas Session.");
331       }
332
333     });
334     udthread.start();
335   }
336
337   /**
338    * leave a session, prompting the user to save if necessary
339    */
340   public void end_session()
341   {
342     end_session(true);
343   }
344
345   private boolean promptUser = true;
346
347   /**
348    * leave a session, optionally prompting the user to save if necessary
349    * 
350    * @param promptUser
351    *          when true enable prompting by this application
352    */
353
354   public void end_session(boolean promptUser)
355   {
356     if (!inSession())
357     {
358       throw new Error(
359               MessageManager
360                       .getString("error.jalview_no_connected_vamsas_session"));
361     }
362     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
363     try
364     {
365       if (joinedSession)
366       {
367         boolean ourprompt = this.promptUser;
368         this.promptUser = promptUser;
369         vclient.finalizeClient();
370         Cache.log.info("Jalview has left the session.");
371         this.promptUser = ourprompt; // restore default value
372       }
373       else
374       {
375         Cache.log
376                 .warn("JV Client leaving a session that's its not joined yet.");
377       }
378       joinedSession = false;
379       vclient = null;
380       app = null;
381       user = null;
382       jv2vobj = null;
383       vobj2jv = null;
384     } catch (Exception e)
385     {
386       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
387     }
388   }
389
390   public void updateJalview(IClientDocument cdoc)
391   {
392     Cache.log.debug("Jalview updating from sesion document ..");
393     ensureJvVamsas();
394     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
395             baseProvEntry(), alRedoState);
396     try
397     {
398       vds.updateToJalview();
399     } catch (Exception e)
400     {
401       Cache.log.error("Failed to update Jalview from vamsas document.", e);
402     }
403     try
404     {
405       if (firstUpdate)
406       {
407         vds.updateJalviewFromAppdata();
408         // Comment this out to repeatedly read in data from JalviewAppData
409         // firstUpdate=false;
410       }
411     } catch (Exception e)
412     {
413       Cache.log.error(
414               "Exception when updating Jalview settings from Appdata.", e);
415     }
416     Cache.log.debug(".. finished updating from sesion document.");
417
418   }
419
420   boolean firstUpdate = false;
421
422   private void ensureJvVamsas()
423   {
424     if (jv2vobj == null)
425     {
426       jv2vobj = new IdentityHashMap();
427       vobj2jv = new Hashtable();
428       alRedoState = new Hashtable();
429       firstUpdate = true;
430     }
431   }
432
433   /**
434    * jalview object binding to VorbaIds
435    */
436   IdentityHashMap jv2vobj = null;
437
438   Hashtable vobj2jv = null;
439
440   Hashtable alRedoState = null;
441
442   boolean errorsDuringUpdate = false;
443
444   boolean errorsDuringAppUpdate = false;
445
446   /**
447    * update the document accessed through doc. A backup of the current object
448    * bindings is made.
449    * 
450    * @param doc
451    * @return number of views stored in document (updated and new views)
452    */
453   public int updateVamsasDocument(IClientDocument doc)
454   {
455     int storedviews = 0;
456     ensureJvVamsas();
457     errorsDuringUpdate = false;
458     errorsDuringAppUpdate = false;
459     backup_objectMapping();
460     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
461             baseProvEntry(), alRedoState);
462     // wander through frames
463     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
464
465     if (frames == null)
466     {
467       return 0;
468     }
469     Hashtable skipList = new Hashtable();
470     Hashtable viewset = new Hashtable();
471
472     try
473     {
474       // REVERSE ORDER
475       for (int i = frames.length - 1; i > -1; i--)
476       {
477         if (frames[i] instanceof AlignFrame)
478         {
479           AlignFrame af = (AlignFrame) frames[i];
480           if (!viewset.containsKey(af.getViewport().getSequenceSetId()))
481           {
482             // update alignment and root from frame.
483             boolean stored = false;
484             try
485             {
486               stored = vds.storeVAMSAS(af.getViewport(), af.getTitle());
487             } catch (Exception e)
488             {
489               errorsDuringUpdate = true;
490               Cache.log.error("Exception synchronizing "
491                       + af.getTitle()
492                       + " "
493                       + (af.getViewport().viewName == null ? "" : " view "
494                               + af.getViewport().viewName)
495                       + " to document.", e);
496               stored = false;
497             }
498             if (!stored)
499             { // record skip in skipList
500               skipList.put(af.getViewport().getSequenceSetId(), af);
501             }
502             else
503             {
504               storedviews++;
505               // could try to eliminate sequenceSetId from skiplist ..
506               // (skipList.containsKey(af.getViewport().getSequenceSetId()))
507               // remember sequenceSetId so we can skip all the other views on
508               // same alignment
509               viewset.put(af.getViewport().getSequenceSetId(), af);
510             }
511           }
512         }
513       }
514       // REVERSE ORDER
515       // for (int i = frames.length - 1; i > -1; i--)
516       // {
517       // if (frames[i] instanceof AlignFrame)
518       // {
519       // AlignFrame af = (AlignFrame) frames[i];
520       Iterator aframes = viewset.values().iterator();
521       while (aframes.hasNext())
522       {
523         AlignFrame af = (AlignFrame) aframes.next();
524         // add any AlignedCodonFrame mappings on this alignment to any other.
525         vds.storeSequenceMappings(af.getViewport(), af.getTitle());
526       }
527     } catch (Exception e)
528     {
529       Cache.log.error("Exception synchronizing Views to Document :", e);
530       errorsDuringUpdate = true;
531     }
532
533     try
534     {
535       if (viewset.size() > 0)
536       {
537         // Alignment views were synchronized, so store their state in the
538         // appData, too.
539         // The skipList ensures we don't write out any alignments not actually
540         // in the document.
541         vds.setSkipList(skipList);
542         vds.updateJalviewClientAppdata();
543       }
544     } catch (Exception e)
545     {
546       Cache.log.error("Client Appdata Write exception", e);
547       errorsDuringAppUpdate = true;
548     }
549     vds.clearSkipList();
550     return storedviews;
551   }
552
553   private Entry baseProvEntry()
554   {
555     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
556     pentry.setUser(user.getFullName());
557     pentry.setApp(app.getClientUrn());
558     pentry.setDate(new java.util.Date());
559     pentry.setAction("created");
560     return pentry;
561   }
562
563   /**
564    * do a vamsas document update or update jalview from the vamsas document
565    * 
566    * @param fromJalview
567    *          true to update from jalview to the vamsas document
568    * @return total number of stored alignments in the document after the update
569    */
570   protected int dealWithDocumentUpdate(boolean fromJalview)
571   {
572     int storedviews = 0;
573     // called by update handler for document update.
574     Cache.log.debug("Updating jalview from changed vamsas document.");
575     disableGui(true);
576     try
577     {
578       long time = System.currentTimeMillis();
579       IClientDocument cdoc = vclient.getClientDocument();
580       if (Cache.log.isDebugEnabled())
581       {
582         Cache.log.debug("Time taken to get ClientDocument = "
583                 + (System.currentTimeMillis() - time));
584         time = System.currentTimeMillis();
585       }
586       if (fromJalview)
587       {
588         storedviews += updateVamsasDocument(cdoc);
589         if (Cache.log.isDebugEnabled())
590         {
591           Cache.log
592                   .debug("Time taken to update Vamsas Document from jalview\t= "
593                           + (System.currentTimeMillis() - time));
594           time = System.currentTimeMillis();
595         }
596         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
597         if (Cache.log.isDebugEnabled())
598         {
599           Cache.log.debug("Time taken to set Document Roots\t\t= "
600                   + (System.currentTimeMillis() - time));
601           time = System.currentTimeMillis();
602         }
603       }
604       else
605       {
606         updateJalview(cdoc);
607         if (Cache.log.isDebugEnabled())
608         {
609           Cache.log
610                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
611                           + (System.currentTimeMillis() - time));
612           time = System.currentTimeMillis();
613         }
614
615       }
616       vclient.updateDocument(cdoc);
617       if (Cache.log.isDebugEnabled())
618       {
619         Cache.log.debug("Time taken to update Session Document\t= "
620                 + (System.currentTimeMillis() - time));
621         time = System.currentTimeMillis();
622       }
623       cdoc = null;
624     } catch (Exception ee)
625     {
626       System.err.println("Exception whilst updating :");
627       ee.printStackTrace(System.err);
628       // recover object map backup, since its probably corrupted with references
629       // to Vobjects that don't exist anymore.
630       recover_objectMappingBackup();
631       storedviews = 0;
632     }
633     Cache.log.debug("Finished updating from document change.");
634     disableGui(false);
635     return storedviews;
636   }
637
638   private void addDocumentUpdateHandler()
639   {
640     final VamsasApplication client = this;
641     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
642     {
643       @Override
644       public void propertyChange(PropertyChangeEvent evt)
645       {
646         Cache.log.debug("Dealing with document update event.");
647         client.dealWithDocumentUpdate(false);
648         Cache.log.debug("finished dealing with event.");
649       }
650     });
651     Cache.log.debug("Added Jalview handler for vamsas document updates.");
652   }
653
654   private void addStoreDocumentHandler()
655   {
656     final VamsasApplication client = this;
657     vclient.addVorbaEventHandler(
658             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
659             new PropertyChangeListener()
660             {
661               @Override
662               public void propertyChange(PropertyChangeEvent evt)
663               {
664                 if (client.promptUser)
665                 {
666                   Cache.log
667                           .debug("Asking user if the vamsas session should be stored.");
668                   int reply = JvOptionPane
669                           .showInternalConfirmDialog(
670                                   Desktop.desktop,
671                                   "The current VAMSAS session has unsaved data - do you want to save it ?",
672                                   "VAMSAS Session Shutdown",
673                                   JvOptionPane.YES_NO_OPTION,
674                                   JvOptionPane.QUESTION_MESSAGE);
675
676                   if (reply == JvOptionPane.YES_OPTION)
677                   {
678                     Cache.log.debug("Prompting for vamsas store filename.");
679                     Desktop.instance.vamsasSave_actionPerformed(null);
680                     Cache.log
681                             .debug("Finished attempt at storing document.");
682                   }
683                   Cache.log
684                           .debug("finished dealing with REQUESTTOCLOSE event.");
685                 }
686                 else
687                 {
688                   Cache.log
689                           .debug("Ignoring store document request (promptUser==false)");
690                 }
691               }
692             });
693     Cache.log.debug("Added Jalview handler for vamsas document updates.");
694   }
695
696   public void disableGui(boolean b)
697   {
698     Desktop.instance.setVamsasUpdate(b);
699   }
700
701   Hashtable _backup_vobj2jv;
702
703   IdentityHashMap _backup_jv2vobj;
704
705   /**
706    * make a backup of the object mappings (vobj2jv and jv2vobj)
707    */
708   public void backup_objectMapping()
709   {
710     _backup_vobj2jv = new Hashtable(vobj2jv);
711     _backup_jv2vobj = new IdentityHashMap(jv2vobj);
712   }
713
714   /**
715    * recover original object mappings from the object mapping backup if document
716    * IO failed
717    * 
718    * @throws Error
719    *           if backup_objectMapping was not called.
720    */
721   public void recover_objectMappingBackup()
722   {
723     if (_backup_vobj2jv == null)
724     {
725       if (inInitialUpdate)
726       {
727         // nothing to recover so just
728         return;
729       }
730
731       throw new Error(
732               MessageManager
733                       .getString("error.implementation_error_cannot_recover_vamsas_object_mappings"));
734     }
735     jv2vobj.clear();
736     Iterator el = _backup_jv2vobj.entrySet().iterator();
737     while (el.hasNext())
738     {
739       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
740       jv2vobj.put(mp.getKey(), mp.getValue());
741     }
742     el = _backup_vobj2jv.entrySet().iterator();
743     while (el.hasNext())
744     {
745       java.util.Map.Entry mp = (java.util.Map.Entry) el.next();
746       vobj2jv.put(mp.getKey(), mp.getValue());
747     }
748   }
749
750   private boolean joinedSession = false;
751
752   private VamsasListener picker = null;
753
754   private SelectionListener selecter;
755
756   private void startSession()
757   {
758     if (inSession())
759     {
760       try
761       {
762         vclient.joinSession();
763         joinedSession = true;
764       } catch (Exception e)
765       {
766         // Complain to GUI
767         Cache.log.error("Failed to join vamsas session.", e);
768         vclient = null;
769       }
770       try
771       {
772         final IPickManager pm = vclient.getPickManager();
773         final StructureSelectionManager ssm = StructureSelectionManager
774                 .getStructureSelectionManager(Desktop.instance);
775         final VamsasApplication me = this;
776         pm.registerMessageHandler(new IMessageHandler()
777         {
778           String last = null;
779
780           @Override
781           public void handleMessage(Message message)
782           {
783             if (vobj2jv == null)
784             {
785               // we are not in a session yet.
786               return;
787             }
788             if (message instanceof MouseOverMessage)
789             {
790               MouseOverMessage mm = (MouseOverMessage) message;
791               String mstring = mm.getVorbaID() + " " + mm.getPosition();
792               if (last != null && mstring.equals(last))
793               {
794                 return;
795               }
796               // if (Cache.log.isDebugEnabled())
797               // {
798               // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
799               // "+mm.getPosition());
800               // }
801               Object jvobj = vobj2jv.get(mm.getVorbaID());
802               if (jvobj != null && jvobj instanceof SequenceI)
803               {
804                 last = mstring;
805                 // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
806                 // bound to "+jvobj+" at "+mm.getPosition());
807                 // position is character position in aligned sequence
808                 ssm.mouseOverVamsasSequence((SequenceI) jvobj,
809                         mm.getPosition(), me);
810               }
811             }
812             if (message instanceof uk.ac.vamsas.client.picking.SelectionMessage)
813             {
814               // we only care about AlignmentSequence selections
815               SelectionMessage sm = (SelectionMessage) message;
816               sm.validate();
817               System.err.println("Received\n" + sm.getRawMessage());
818               Object[] jvobjs = sm.getVorbaIDs() == null ? null
819                       : new Object[sm.getVorbaIDs().length];
820               if (jvobjs == null)
821               {
822                 // TODO: rationalise : can only clear a selection over a
823                 // referred to object
824                 ssm.sendSelection(null, null, null, me);
825                 return;
826               }
827               Class type = null;
828               boolean send = true;
829               for (int o = 0; o < jvobjs.length; o++)
830               {
831                 jvobjs[o] = vobj2jv.get(sm.getVorbaIDs()[o]);
832                 if (jvobjs[o] == null)
833                 {
834                   // can't cope with selections for unmapped objects
835                   continue;
836                 }
837                 if (type == null)
838                 {
839                   type = jvobjs[o].getClass();
840                 }
841                 ;
842                 if (type != jvobjs[o].getClass())
843                 {
844                   send = false;
845                   // discard - can't cope with selections over mixed objects
846                   // continue;
847                 }
848               }
849               SequenceGroup jselection = null;
850               ColumnSelection colsel = null;
851               if (type == jalview.datamodel.Alignment.class)
852               {
853                 if (jvobjs.length == 1)
854                 {
855                   // TODO if (sm.isNone())// send a message to select the
856                   // specified columns over the
857                   // given
858                   // alignment
859
860                   send = true;
861                 }
862               }
863               if (type == jalview.datamodel.Sequence.class)
864               {
865
866                 SequenceI seq;
867                 boolean aligned = ((jalview.datamodel.Sequence) jvobjs[0])
868                         .getDatasetSequence() != null;
869                 int maxWidth = 0;
870                 if (aligned)
871                 {
872                   jselection = new SequenceGroup();
873                   jselection.addSequence(
874                           seq = (jalview.datamodel.Sequence) jvobjs[0],
875                           false);
876                   maxWidth = seq.getLength();
877                 }
878                 for (int c = 1; aligned && jvobjs.length > 1
879                         && c < jvobjs.length; c++)
880                 {
881                   if (((jalview.datamodel.Sequence) jvobjs[c])
882                           .getDatasetSequence() == null)
883                   {
884                     aligned = false;
885                     continue;
886                   }
887                   else
888                   {
889                     jselection.addSequence(
890                             seq = (jalview.datamodel.Sequence) jvobjs[c],
891                             false);
892                     if (maxWidth < seq.getLength())
893                     {
894                       maxWidth = seq.getLength();
895                     }
896
897                   }
898                 }
899                 if (!aligned)
900                 {
901                   jselection = null;
902                   // if cardinality is greater than one then verify all
903                   // sequences are alignment sequences.
904                   if (jvobjs.length == 1)
905                   {
906                     // find all instances of this dataset sequence in the
907                     // displayed alignments containing the associated range and
908                     // select them.
909                   }
910                 }
911                 else
912                 {
913                   jselection.setStartRes(0);
914                   jselection.setEndRes(maxWidth);
915                   // locate the alignment containing the given sequences and
916                   // select the associated ranges on them.
917                   if (sm.getRanges() != null)
918                   {
919                     int[] prange = uk.ac.vamsas.objects.utils.Range
920                             .getBounds(sm.getRanges());
921                     jselection.setStartRes(prange[0] - 1);
922                     jselection.setEndRes(prange[1] - 1);
923                     prange = uk.ac.vamsas.objects.utils.Range
924                             .getIntervals(sm.getRanges());
925                     colsel = new ColumnSelection();
926                     for (int p = 0; p < prange.length; p += 2)
927                     {
928                       int d = (prange[p] <= prange[p + 1]) ? 1 : -1;
929                       // try to join up adjacent columns to make a larger
930                       // selection
931                       // lower and upper bounds
932                       int l = (d < 0) ? 1 : 0;
933                       int u = (d > 0) ? 1 : 0;
934
935                       if (jselection.getStartRes() > 0
936                               && prange[p + l] == jselection.getStartRes())
937                       {
938                         jselection.setStartRes(prange[p + l] - 1);
939                       }
940                       if (jselection.getEndRes() <= maxWidth
941                               && prange[p + u] == (jselection.getEndRes() + 2))
942                       {
943                         jselection.setEndRes(prange[p + u] - 1);
944                       }
945                       // mark all the columns in the range.
946                       for (int sr = prange[p], er = prange[p + 1], de = er
947                               + d; sr != de; sr += d)
948                       {
949                         colsel.addElement(sr - 1);
950                       }
951                     }
952                   }
953                   send = true;
954                 }
955               }
956               if (send)
957               {
958                 ssm.sendSelection(jselection, colsel, null, me);
959               }
960               // discard message.
961               for (int c = 0; c < jvobjs.length; c++)
962               {
963                 jvobjs[c] = null;
964               }
965               ;
966               jvobjs = null;
967               return;
968             }
969           }
970         });
971         picker = new VamsasListener()
972         {
973           SequenceI last = null;
974
975           int i = -1;
976
977           @Override
978           public void mouseOverSequence(SequenceI seq, int index,
979                   VamsasSource source)
980           {
981             if (jv2vobj == null)
982             {
983               return;
984             }
985             if (seq != last || i != index)
986             {
987               VorbaId v = (VorbaId) jv2vobj.get(seq);
988               if (v != null)
989               {
990                 // this should really be a trace message.
991                 // Cache.log.debug("Mouse over " + v.getId() + " bound to "
992                 // + seq + " at " + index);
993                 last = seq;
994                 i = index;
995                 MouseOverMessage message = new MouseOverMessage(v.getId(),
996                         index);
997                 pm.sendMessage(message);
998               }
999             }
1000           }
1001         };
1002         selecter = new SelectionListener()
1003         {
1004
1005           @Override
1006           public void selection(SequenceGroup seqsel,
1007                   ColumnSelection colsel, HiddenColumns hidden,
1008                   SelectionSource source)
1009           {
1010             if (vobj2jv == null)
1011             {
1012               Cache.log
1013                       .warn("Selection listener still active for dead session.");
1014               // not in a session.
1015               return;
1016             }
1017             if (source != me)
1018             {
1019               AlignmentI visal = null;
1020               if (source instanceof AlignViewport)
1021               {
1022                 visal = ((AlignmentViewport) source).getAlignment();
1023               }
1024               SelectionMessage sm = null;
1025               if ((seqsel == null || seqsel.getSize() == 0)
1026                       && (colsel == null || colsel.getSelected() == null || colsel
1027                               .getSelected().size() == 0))
1028               {
1029                 if (source instanceof AlignViewport)
1030                 {
1031                   // the empty selection.
1032                   sm = new SelectionMessage("jalview",
1033                           new String[] { ((AlignmentViewport) source)
1034                                   .getSequenceSetId() }, null, true);
1035                 }
1036                 else
1037                 {
1038                   // the empty selection.
1039                   sm = new SelectionMessage("jalview", null, null, true);
1040                 }
1041               }
1042               else
1043               {
1044                 String[] vobj = new String[seqsel.getSize()];
1045                 int o = 0;
1046                 for (SequenceI sel : seqsel.getSequences(null))
1047                 {
1048                   VorbaId v = (VorbaId) jv2vobj.get(sel);
1049                   if (v != null)
1050                   {
1051                     vobj[o++] = v.toString();
1052                   }
1053                 }
1054                 if (o < vobj.length)
1055                 {
1056                   String t[] = vobj;
1057                   vobj = new String[o];
1058                   System.arraycopy(t, 0, vobj, 0, o);
1059                   t = null;
1060                 }
1061                 Input range = null;
1062                 if (seqsel != null && colsel != null)
1063                 {
1064                   // deparse the colsel into positions on the vamsas alignment
1065                   // sequences
1066                   range = new Input();
1067                   if (colsel.getSelected() != null
1068                           && colsel.getSelected().size() > 0
1069                           && visal != null
1070                           && seqsel.getSize() == visal.getHeight())
1071                   {
1072                     // gather selected columns outwith the sequence positions
1073                     // too
1074                     for (Integer ival : colsel.getSelected())
1075                     {
1076                       Pos p = new Pos();
1077                       p.setI(ival.intValue() + 1);
1078                       range.addPos(p);
1079                     }
1080                   }
1081                   else
1082                   {
1083                     // int[] intervals = colsel.getVisibleContigs(
1084                     // seqsel.getStartRes(), seqsel.getEndRes() + 1);
1085                     int[] intervals = hidden.getVisibleContigs(
1086                             seqsel.getStartRes(), seqsel.getEndRes() + 1);
1087                     for (int iv = 0; iv < intervals.length; iv += 2)
1088                     {
1089                       Seg s = new Seg();
1090                       s.setStart(intervals[iv] + 1); // vamsas indices begin at
1091                       // 1, not zero.
1092                       s.setEnd(intervals[iv + 1] + 1);
1093                       s.setInclusive(true);
1094                       range.addSeg(s);
1095                     }
1096                   }
1097                 }
1098                 if (vobj.length > 0)
1099                 {
1100                   sm = new SelectionMessage("jalview", vobj, range);
1101                 }
1102                 else
1103                 {
1104                   sm = null;
1105                 }
1106               }
1107               if (sm != null)
1108               {
1109                 sm.validate(); // debug
1110                 Cache.log.debug("Selection Message\n" + sm.getRawMessage());
1111                 pm.sendMessage(sm);
1112               }
1113             }
1114           }
1115
1116         };
1117         ssm.addStructureViewerListener(picker); // better method here
1118         ssm.addSelectionListener(selecter);
1119       } catch (Exception e)
1120       {
1121         Cache.log.error("Failed to init Vamsas Picking", e);
1122       }
1123     }
1124   }
1125
1126   public String getCurrentSession()
1127   {
1128     if (vclient != null)
1129     {
1130       return (vclient.getSessionUrn());
1131     }
1132     return null;
1133   }
1134 }