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