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