Merge develop to Release_2_8_3_Branch
[jalview.git] / src / jalview / gui / VamsasApplication.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.SequenceGroup;
27 import jalview.datamodel.SequenceI;
28 import jalview.io.VamsasAppDatastore;
29 import jalview.structure.SelectionListener;
30 import jalview.structure.SelectionSource;
31 import jalview.structure.StructureSelectionManager;
32 import jalview.structure.VamsasListener;
33 import jalview.structure.VamsasSource;
34 import jalview.util.MessageManager;
35 import jalview.viewmodel.AlignmentViewport;
36
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.Hashtable;
42 import java.util.IdentityHashMap;
43 import java.util.Iterator;
44
45 import javax.swing.JInternalFrame;
46 import javax.swing.JOptionPane;
47
48 import uk.ac.vamsas.client.ClientHandle;
49 import uk.ac.vamsas.client.IClient;
50 import uk.ac.vamsas.client.IClientDocument;
51 import uk.ac.vamsas.client.InvalidSessionDocumentException;
52 import uk.ac.vamsas.client.UserHandle;
53 import uk.ac.vamsas.client.VorbaId;
54 import uk.ac.vamsas.client.picking.IMessageHandler;
55 import uk.ac.vamsas.client.picking.IPickManager;
56 import uk.ac.vamsas.client.picking.Message;
57 import uk.ac.vamsas.client.picking.MouseOverMessage;
58 import uk.ac.vamsas.client.picking.SelectionMessage;
59 import uk.ac.vamsas.objects.core.Entry;
60 import uk.ac.vamsas.objects.core.Input;
61 import uk.ac.vamsas.objects.core.Pos;
62 import uk.ac.vamsas.objects.core.Seg;
63
64 /**
65  * @author jimp
66  * 
67  */
68 public class VamsasApplication implements SelectionSource, VamsasSource
69 {
70   IClient vclient = null;
71
72   ClientHandle app = null;
73
74   UserHandle user = null;
75
76   Desktop jdesktop = null; // our jalview desktop reference
77
78   private boolean inInitialUpdate = true;
79
80   // Cache.preferences for vamsas client session arena
81   // preferences for check for default session at startup.
82   // user and organisation stuff.
83   public VamsasApplication(Desktop jdesktop, File sessionPath,
84           String sessionName)
85   {
86     // JBPNote:
87     // we should create a session URI from the sessionPath and pass it to
88     // the clientFactory - but the vamsas api doesn't cope with that yet.
89     this.jdesktop = jdesktop;
90     initClientSession(null, sessionPath, sessionName);
91   }
92
93   private static uk.ac.vamsas.client.IClientFactory getClientFactory()
94           throws IOException
95   {
96     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
97   }
98
99   /**
100    * Start a new vamsas session
101    * 
102    * @param jdesktop
103    */
104   public VamsasApplication(Desktop jdesktop)
105   {
106     this.jdesktop = jdesktop;
107     initClientSession(null, null);
108   }
109
110   /**
111    * init a connection to the session at the given url
112    * 
113    * @param jdesktop
114    * @param sessionUrl
115    */
116   public VamsasApplication(Desktop jdesktop, String sessionUrl)
117   {
118     this.jdesktop = jdesktop;
119     initClientSession(sessionUrl, null);
120   }
121
122   /**
123    * @throws IOException
124    *           or other if clientfactory instantiation failed.
125    * @return list of current sessions or null if no session exists.
126    */
127   public static String[] getSessionList() throws Exception
128   {
129     return getClientFactory().getCurrentSessions();
130   }
131
132   /**
133    * initialise, possibly with either a valid session url or a file for a new
134    * session
135    * 
136    * @param sess
137    *          null or a valid session url
138    * @param vamsasDocument
139    *          null or a valid vamsas document file
140    * @return false if no vamsas connection was made
141    */
142   private void initClientSession(String sess, File vamsasDocument)
143   {
144     initClientSession(sess, vamsasDocument, null);
145   }
146
147   private boolean initClientSession(String sess, File vamsasDocument,
148           String newDocSessionName)
149   {
150     try
151     {
152       // Only need to tell the library what the application is here
153       app = getJalviewHandle();
154       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
155       if (vamsasDocument != null)
156       {
157         if (sess != null)
158         {
159           throw new Error(MessageManager.getString("error.implementation_error_cannot_import_vamsas_doc"));
160         }
161         try
162         {
163           if (newDocSessionName != null)
164           {
165             vclient = clientfactory.openAsNewSessionIClient(app,
166                     vamsasDocument, newDocSessionName);
167           }
168           else
169           {
170             vclient = clientfactory.openAsNewSessionIClient(app,
171                     vamsasDocument);
172           }
173         } catch (InvalidSessionDocumentException e)
174         {
175           JOptionPane
176                   .showInternalMessageDialog(
177                           Desktop.desktop,
178
179                           MessageManager.getString("label.vamsas_doc_couldnt_be_opened_as_new_session"),
180                           MessageManager.getString("label.vamsas_document_import_failed"),
181                           JOptionPane.ERROR_MESSAGE);
182
183         }
184       }
185       else
186       {
187         // join existing or create a new session
188         if (sess == null)
189         {
190           vclient = clientfactory.getNewSessionIClient(app);
191         }
192         else
193         {
194           vclient = clientfactory.getIClient(app, sess);
195         }
196       }
197       // set some properties for our VAMSAS interaction
198       setVclientConfig();
199       user = vclient.getUserHandle();
200
201     } catch (Exception e)
202     {
203       jalview.bin.Cache.log
204               .error("Couldn't instantiate vamsas client !", e);
205       return false;
206     }
207     return true;
208   }
209
210   private void setVclientConfig()
211   {
212     if (vclient == null)
213     {
214       return;
215     }
216     try
217     {
218       if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
219       {
220         uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient) vclient)
221                 .getSimpleClientConfig();
222         cfg._validatemergedroots = false;
223         cfg._validateupdatedroots = true; // we may write rubbish otherwise.
224       }
225     } catch (Error e)
226     {
227       Cache.log
228               .warn("Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",
229                       e);
230     } catch (Exception e)
231     {
232       Cache.log
233               .warn("Probable VAMSAS client incompatibility - carrying on regardless",
234                       e);
235     }
236   }
237
238   /**
239    * make the appHandle for Jalview
240    * 
241    * @return
242    */
243   private ClientHandle getJalviewHandle()
244   {
245     return new ClientHandle("jalview.bin.Jalview",
246             jalview.bin.Cache.getProperty("VERSION"));
247   }
248
249   /**
250    * 
251    * @return true if we are registered in a vamsas session
252    */
253   public boolean inSession()
254   {
255     return (vclient != null);
256   }
257
258   /**
259    * called to connect to session inits handlers, does an initial document
260    * update.
261    */
262   public void initial_update()
263   {
264     if (!inSession())
265     {
266       throw new Error(MessageManager.getString("error.implementation_error_vamsas_operation_not_init"));
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("Exception whilst refreshing jalview windows after a vamsas document update.",
309                       e);
310     }
311   }
312
313   public void push_update()
314   {
315     Thread udthread = new Thread(new Runnable()
316     {
317
318       public void run()
319       {
320         Cache.log.info("Jalview updating to the Vamsas Session.");
321
322         dealWithDocumentUpdate(true);
323         Cache.log.info("Jalview finished updating to the Vamsas Session.");
324       }
325
326     });
327     udthread.start();
328   }
329
330   /**
331    * leave a session, prompting the user to save if necessary
332    */
333   public void end_session()
334   {
335     end_session(true);
336   }
337
338   private boolean promptUser = true;
339
340   /**
341    * leave a session, optionally prompting the user to save if necessary
342    * 
343    * @param promptUser
344    *          when true enable prompting by this application
345    */
346
347   public void end_session(boolean promptUser)
348   {
349     if (!inSession())
350     {
351       throw new Error(MessageManager.getString("error.jalview_no_connected_vamsas_session"));
352     }
353     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
354     try
355     {
356       if (joinedSession)
357       {
358         boolean ourprompt = this.promptUser;
359         this.promptUser = promptUser;
360         vclient.finalizeClient();
361         Cache.log.info("Jalview has left the session.");
362         this.promptUser = ourprompt; // restore default value
363       }
364       else
365       {
366         Cache.log
367                 .warn("JV Client leaving a session that's its not joined yet.");
368       }
369       joinedSession = false;
370       vclient = null;
371       app = null;
372       user = null;
373       jv2vobj = null;
374       vobj2jv = null;
375     } catch (Exception e)
376     {
377       Cache.log.error("Vamsas Session finalization threw exceptions!", e);
378     }
379   }
380
381   public void updateJalview(IClientDocument cdoc)
382   {
383     Cache.log.debug("Jalview updating from sesion document ..");
384     ensureJvVamsas();
385     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
386             baseProvEntry(), alRedoState);
387     try
388     {
389       vds.updateToJalview();
390     } catch (Exception e)
391     {
392       Cache.log.error("Failed to update Jalview from vamsas document.", e);
393     }
394     try
395     {
396       if (firstUpdate)
397       {
398         vds.updateJalviewFromAppdata();
399         // Comment this out to repeatedly read in data from JalviewAppData
400         // firstUpdate=false;
401       }
402     } catch (Exception e)
403     {
404       Cache.log.error(
405               "Exception when updating Jalview settings from Appdata.", e);
406     }
407     Cache.log.debug(".. finished updating from sesion document.");
408
409   }
410
411   boolean firstUpdate = false;
412
413   private void ensureJvVamsas()
414   {
415     if (jv2vobj == null)
416     {
417       jv2vobj = new IdentityHashMap();
418       vobj2jv = new Hashtable();
419       alRedoState = new Hashtable();
420       firstUpdate = true;
421     }
422   }
423
424   /**
425    * jalview object binding to VorbaIds
426    */
427   IdentityHashMap jv2vobj = null;
428
429   Hashtable vobj2jv = null;
430
431   Hashtable alRedoState = null;
432
433   boolean errorsDuringUpdate = false;
434
435   boolean errorsDuringAppUpdate = false;
436
437   /**
438    * update the document accessed through doc. A backup of the current object
439    * bindings is made.
440    * 
441    * @param doc
442    * @return number of views stored in document (updated and new views)
443    */
444   public int updateVamsasDocument(IClientDocument doc)
445   {
446     int storedviews = 0;
447     ensureJvVamsas();
448     errorsDuringUpdate = false;
449     errorsDuringAppUpdate = false;
450     backup_objectMapping();
451     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
452             baseProvEntry(), alRedoState);
453     // wander through frames
454     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
455
456     if (frames == null)
457     {
458       return 0;
459     }
460     Hashtable skipList = new Hashtable();
461     Hashtable viewset = new Hashtable();
462
463     try
464     {
465       // REVERSE ORDER
466       for (int i = frames.length - 1; i > -1; i--)
467       {
468         if (frames[i] instanceof AlignFrame)
469         {
470           AlignFrame af = (AlignFrame) frames[i];
471           if (!viewset.containsKey(af.getViewport().getSequenceSetId()))
472           {
473             // update alignment and root from frame.
474             boolean stored = false;
475             try
476             {
477               stored = vds.storeVAMSAS(af.getViewport(), af.getTitle());
478             } catch (Exception e)
479             {
480               errorsDuringUpdate = true;
481               Cache.log.error("Exception synchronizing "
482                       + af.getTitle()
483                       + " "
484                       + (af.getViewport().viewName == null ? "" : " view "
485                               + af.getViewport().viewName)
486                       + " to document.", e);
487               stored = false;
488             }
489             if (!stored)
490             { // record skip in skipList
491               skipList.put(af.getViewport().getSequenceSetId(), af);
492             }
493             else
494             {
495               storedviews++;
496               // could try to eliminate sequenceSetId from skiplist ..
497               // (skipList.containsKey(af.getViewport().getSequenceSetId()))
498               // remember sequenceSetId so we can skip all the other views on
499               // same alignment
500               viewset.put(af.getViewport().getSequenceSetId(), af);
501             }
502           }
503         }
504       }
505       // REVERSE ORDER
506       // for (int i = frames.length - 1; i > -1; i--)
507       // {
508       // if (frames[i] instanceof AlignFrame)
509       // {
510       // AlignFrame af = (AlignFrame) frames[i];
511       Iterator aframes = viewset.values().iterator();
512       while (aframes.hasNext())
513       {
514         AlignFrame af = (AlignFrame) aframes.next();
515         // add any AlignedCodonFrame mappings on this alignment to any other.
516         vds.storeSequenceMappings(af.getViewport(), af.getTitle());
517       }
518     } catch (Exception e)
519     {
520       Cache.log.error("Exception synchronizing Views to Document :", e);
521       errorsDuringUpdate = true;
522     }
523
524     try
525     {
526       if (viewset.size() > 0)
527       {
528         // Alignment views were synchronized, so store their state in the
529         // appData, too.
530         // The skipList ensures we don't write out any alignments not actually
531         // in the document.
532         vds.setSkipList(skipList);
533         vds.updateJalviewClientAppdata();
534       }
535     } catch (Exception e)
536     {
537       Cache.log.error("Client Appdata Write exception", e);
538       errorsDuringAppUpdate = true;
539     }
540     vds.clearSkipList();
541     return storedviews;
542   }
543
544   private Entry baseProvEntry()
545   {
546     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
547     pentry.setUser(user.getFullName());
548     pentry.setApp(app.getClientUrn());
549     pentry.setDate(new java.util.Date());
550     pentry.setAction("created");
551     return pentry;
552   }
553
554   /**
555    * do a vamsas document update or update jalview from the vamsas document
556    * 
557    * @param fromJalview
558    *          true to update from jalview to the vamsas document
559    * @return total number of stored alignments in the document after the update
560    */
561   protected int dealWithDocumentUpdate(boolean fromJalview)
562   {
563     int storedviews = 0;
564     // called by update handler for document update.
565     Cache.log.debug("Updating jalview from changed vamsas document.");
566     disableGui(true);
567     try
568     {
569       long time = System.currentTimeMillis();
570       IClientDocument cdoc = vclient.getClientDocument();
571       if (Cache.log.isDebugEnabled())
572       {
573         Cache.log.debug("Time taken to get ClientDocument = "
574                 + (System.currentTimeMillis() - time));
575         time = System.currentTimeMillis();
576       }
577       if (fromJalview)
578       {
579         storedviews += updateVamsasDocument(cdoc);
580         if (Cache.log.isDebugEnabled())
581         {
582           Cache.log
583                   .debug("Time taken to update Vamsas Document from jalview\t= "
584                           + (System.currentTimeMillis() - time));
585           time = System.currentTimeMillis();
586         }
587         cdoc.setVamsasRoots(cdoc.getVamsasRoots());
588         if (Cache.log.isDebugEnabled())
589         {
590           Cache.log.debug("Time taken to set Document Roots\t\t= "
591                   + (System.currentTimeMillis() - time));
592           time = System.currentTimeMillis();
593         }
594       }
595       else
596       {
597         updateJalview(cdoc);
598         if (Cache.log.isDebugEnabled())
599         {
600           Cache.log
601                   .debug("Time taken to update Jalview from vamsas document Roots\t= "
602                           + (System.currentTimeMillis() - time));
603           time = System.currentTimeMillis();
604         }
605
606       }
607       vclient.updateDocument(cdoc);
608       if (Cache.log.isDebugEnabled())
609       {
610         Cache.log.debug("Time taken to update Session Document\t= "
611                 + (System.currentTimeMillis() - time));
612         time = System.currentTimeMillis();
613       }
614       cdoc = null;
615     } catch (Exception ee)
616     {
617       System.err.println("Exception whilst updating :");
618       ee.printStackTrace(System.err);
619       // recover object map backup, since its probably corrupted with references
620       // to Vobjects that don't exist anymore.
621       recover_objectMappingBackup();
622       storedviews = 0;
623     }
624     Cache.log.debug("Finished updating from document change.");
625     disableGui(false);
626     return storedviews;
627   }
628
629   private void addDocumentUpdateHandler()
630   {
631     final VamsasApplication client = this;
632     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
633     {
634       public void propertyChange(PropertyChangeEvent evt)
635       {
636         Cache.log.debug("Dealing with document update event.");
637         client.dealWithDocumentUpdate(false);
638         Cache.log.debug("finished dealing with event.");
639       }
640     });
641     Cache.log.debug("Added Jalview handler for vamsas document updates.");
642   }
643
644   private void addStoreDocumentHandler()
645   {
646     final VamsasApplication client = this;
647     vclient.addVorbaEventHandler(
648             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
649             new PropertyChangeListener()
650             {
651               public void propertyChange(PropertyChangeEvent evt)
652               {
653                 if (client.promptUser)
654                 {
655                   Cache.log
656                           .debug("Asking user if the vamsas session should be stored.");
657                   int reply = JOptionPane
658                           .showInternalConfirmDialog(
659                                   Desktop.desktop,
660                                   "The current VAMSAS session has unsaved data - do you want to save it ?",
661                                   "VAMSAS Session Shutdown",
662                                   JOptionPane.YES_NO_OPTION,
663                                   JOptionPane.QUESTION_MESSAGE);
664
665                   if (reply == JOptionPane.YES_OPTION)
666                   {
667                     Cache.log.debug("Prompting for vamsas store filename.");
668                     Desktop.instance.vamsasSave_actionPerformed(null);
669                     Cache.log
670                             .debug("Finished attempt at storing document.");
671                   }
672                   Cache.log
673                           .debug("finished dealing with REQUESTTOCLOSE event.");
674                 }
675                 else
676                 {
677                   Cache.log
678                           .debug("Ignoring store document request (promptUser==false)");
679                 }
680               }
681             });
682     Cache.log.debug("Added Jalview handler for vamsas document updates.");
683   }
684
685   public void disableGui(boolean b)
686   {
687     Desktop.instance.setVamsasUpdate(b);
688   }
689
690   Hashtable _backup_vobj2jv;
691
692   IdentityHashMap _backup_jv2vobj;
693
694   /**
695    * make a backup of the object mappings (vobj2jv and jv2vobj)
696    */
697   public void backup_objectMapping()
698   {
699     _backup_vobj2jv = new Hashtable(vobj2jv);
700     _backup_jv2vobj = new IdentityHashMap(jv2vobj);
701   }
702
703   /**
704    * recover original object mappings from the object mapping backup if document
705    * IO failed
706    * 
707    * @throws Error
708    *           if backup_objectMapping was not called.
709    */
710   public void recover_objectMappingBackup()
711   {
712     if (_backup_vobj2jv == null)
713     {
714       if (inInitialUpdate)
715       {
716         // nothing to recover so just
717         return;
718       }
719
720       throw new Error(MessageManager.getString("error.implementation_error_cannot_recover_vamsas_object_mappings"));
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(Desktop.instance);
762         final VamsasApplication 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,
795                         mm.getPosition(), me);
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           @Override
964           public void mouseOverSequence(SequenceI seq, int index,
965                   VamsasSource source)
966           {
967             if (jv2vobj == null)
968             {
969               return;
970             }
971             if (seq != last || i != index)
972             {
973               VorbaId v = (VorbaId) jv2vobj.get(seq);
974               if (v != null)
975               {
976                 // this should really be a trace message.
977                 // Cache.log.debug("Mouse over " + v.getId() + " bound to "
978                 // + seq + " at " + index);
979                 last = seq;
980                 i = index;
981                 MouseOverMessage message = new MouseOverMessage(v.getId(),
982                         index);
983                 pm.sendMessage(message);
984               }
985             }
986           }
987         };
988         selecter = new SelectionListener()
989         {
990
991           public void selection(SequenceGroup seqsel,
992                   ColumnSelection colsel, SelectionSource source)
993           {
994             if (vobj2jv == null)
995             {
996               Cache.log
997                       .warn("Selection listener still active for dead session.");
998               // not in a session.
999               return;
1000             }
1001             if (source != me)
1002             {
1003               AlignmentI visal = null;
1004               if (source instanceof AlignViewport)
1005               {
1006                 visal = ((AlignmentViewport) source).getAlignment();
1007               }
1008               SelectionMessage sm = null;
1009               if ((seqsel == null || seqsel.getSize() == 0)
1010                       && (colsel == null || colsel.getSelected() == null || colsel
1011                               .getSelected().size() == 0))
1012               {
1013                 if (source instanceof AlignViewport)
1014                 {
1015                   // the empty selection.
1016                   sm = new SelectionMessage("jalview", new String[]
1017                   { ((AlignmentViewport) source).getSequenceSetId() }, null,
1018                           true);
1019                 }
1020                 else
1021                 {
1022                   // the empty selection.
1023                   sm = new SelectionMessage("jalview", null, null, true);
1024                 }
1025               }
1026               else
1027               {
1028                 String[] vobj = new String[seqsel.getSize()];
1029                 int o = 0;
1030                 for (SequenceI sel : seqsel.getSequences(null))
1031                 {
1032                   VorbaId v = (VorbaId) jv2vobj.get(sel);
1033                   if (v != null)
1034                   {
1035                     vobj[o++] = v.toString();
1036                   }
1037                 }
1038                 if (o < vobj.length)
1039                 {
1040                   String t[] = vobj;
1041                   vobj = new String[o];
1042                   System.arraycopy(t, 0, vobj, 0, o);
1043                   t = null;
1044                 }
1045                 Input range = null;
1046                 if (seqsel != null && colsel != null)
1047                 {
1048                   // deparse the colsel into positions on the vamsas alignment
1049                   // sequences
1050                   range = new Input();
1051                   if (colsel.getSelected() != null
1052                           && colsel.getSelected().size() > 0
1053                           && visal != null
1054                           && seqsel.getSize() == visal.getHeight())
1055                   {
1056                     // gather selected columns outwith the sequence positions
1057                     // too
1058                     for (Object obj : colsel.getSelected())
1059                     {
1060                       int ival = ((Integer) obj).intValue();
1061                       Pos p = new Pos();
1062                       p.setI(ival + 1);
1063                       range.addPos(p);
1064                     }
1065                   }
1066                   else
1067                   {
1068                     int[] intervals = colsel.getVisibleContigs(
1069                             seqsel.getStartRes(), seqsel.getEndRes() + 1);
1070                     for (int iv = 0; iv < intervals.length; iv += 2)
1071                     {
1072                       Seg s = new Seg();
1073                       s.setStart(intervals[iv] + 1); // vamsas indices begin at
1074                       // 1, not zero.
1075                       s.setEnd(intervals[iv + 1] + 1);
1076                       s.setInclusive(true);
1077                       range.addSeg(s);
1078                     }
1079                   }
1080                 }
1081                 if (vobj.length > 0)
1082                 {
1083                   sm = new SelectionMessage("jalview", vobj, range);
1084                 }
1085                 else
1086                 {
1087                   sm = null;
1088                 }
1089               }
1090               if (sm != null)
1091               {
1092                 sm.validate(); // debug
1093                 Cache.log.debug("Selection Message\n" + sm.getRawMessage());
1094                 pm.sendMessage(sm);
1095               }
1096             }
1097           }
1098
1099         };
1100         ssm.addStructureViewerListener(picker); // better method here
1101         ssm.addSelectionListener(selecter);
1102       } catch (Exception e)
1103       {
1104         Cache.log.error("Failed to init Vamsas Picking", e);
1105       }
1106     }
1107   }
1108
1109   public String getCurrentSession()
1110   {
1111     if (vclient != null)
1112     {
1113       return (vclient.getSessionUrn());
1114     }
1115     return null;
1116   }
1117 }