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