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