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