vamsas connects and updates are threaded with a progress bar.
[jalview.git] / src / jalview / gui / VamsasApplication.java
index 62e2d21..bfe5487 100644 (file)
  */
 package jalview.gui;
 
+import jalview.bin.Cache;
+import jalview.datamodel.SequenceI;
+import jalview.io.VamsasAppDatastore;
+import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasListener;
+
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
 import java.util.Hashtable;
 import java.util.IdentityHashMap;
-import java.util.Vector;
-import java.util.jar.JarOutputStream;
 
 import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
 
-import jalview.bin.Cache;
-import jalview.io.VamsasAppDatastore;
-import jalview.io.VamsasDatastore;
-
-import uk.ac.vamsas.client.*;
-import uk.ac.vamsas.*;
-import uk.ac.vamsas.objects.*;
+import uk.ac.vamsas.client.ClientHandle;
+import uk.ac.vamsas.client.IClient;
+import uk.ac.vamsas.client.IClientDocument;
+import uk.ac.vamsas.client.InvalidSessionDocumentException;
+import uk.ac.vamsas.client.NoDefaultSessionException;
+import uk.ac.vamsas.client.UserHandle;
+import uk.ac.vamsas.client.VorbaId;
+import uk.ac.vamsas.client.picking.IMessageHandler;
+import uk.ac.vamsas.client.picking.IPickManager;
+import uk.ac.vamsas.client.picking.Message;
+import uk.ac.vamsas.client.picking.MouseOverMessage;
 import uk.ac.vamsas.objects.core.Entry;
+
 /**
  * @author jimp
- *
+ * 
  */
 public class VamsasApplication
 {
-  IClient vclient=null;
-  ClientHandle app=null;
-  UserHandle user=null;
-  
+  IClient vclient = null;
+
+  ClientHandle app = null;
+
+  UserHandle user = null;
+
   Desktop jdesktop = null; // our jalview desktop reference
-  
+
   // Cache.preferences for vamsas client session arena
   // preferences for check for default session at startup.
   // user and organisation stuff.
-  public VamsasApplication(Desktop jdesktop,
-                      File sessionPath)
+  public VamsasApplication(Desktop jdesktop, File sessionPath)
   {
     // JBPNote:
     // we should create a session URI from the sessionPath and pass it to
     // the clientFactory - but the vamsas api doesn't cope with that yet.
-    this(jdesktop);
-    throw new Error("Sorry - can't start from session file yet."); // make this a warning
+    this.jdesktop = jdesktop;
+    initClientSession(null, sessionPath);
   }
-  private static uk.ac.vamsas.client.IClientFactory getClientFactory() throws IOException {
+
+  private static uk.ac.vamsas.client.IClientFactory getClientFactory()
+          throws IOException
+  {
     return new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
   }
+
+  /**
+   * Start a new vamsas session
+   * 
+   * @param jdesktop
+   */
   public VamsasApplication(Desktop jdesktop)
   {
-    this.jdesktop = jdesktop; 
-    initClientSession(null);
+    this.jdesktop = jdesktop;
+    initClientSession(null, null);
+  }
+
+  /**
+   * init a connection to the session at the given url
+   * 
+   * @param jdesktop
+   * @param sessionUrl
+   */
+  public VamsasApplication(Desktop jdesktop, String sessionUrl)
+  {
+    this.jdesktop = jdesktop;
+    initClientSession(sessionUrl, null);
   }
+
   /**
-   * @throws IOException or other if clientfactory instantiation failed.
+   * @throws IOException
+   *                 or other if clientfactory instantiation failed.
    * @return list of current sessions or null if no session exists.
    */
-  public static String[] getSessionList() throws Exception {
+  public static String[] getSessionList() throws Exception
+  {
     return getClientFactory().getCurrentSessions();
   }
+
   /**
-   * initialise, possibly with e valid session url
+   * initialise, possibly with either a valid session url or a file for a new
+   * session
+   * 
    * @param sess
-   * @return
+   *                null or a valid session url
+   * @param vamsasDocument
+   *                null or a valid vamsas document file
+   * @return false if no vamsas connection was made
    */
-  private boolean initClientSession(String sess) {
-    try {
+  private boolean initClientSession(String sess, File vamsasDocument)
+  {
+    try
+    {
       // Only need to tell the library what the application is here
-      app = new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache.getProperty("VERSION"));
+      app = getJalviewHandle();
       uk.ac.vamsas.client.IClientFactory clientfactory = getClientFactory();
-      if (sess==null)
+      if (vamsasDocument != null)
       {
-        vclient = clientfactory.getIClient(app);
+        if (sess != null)
+        {
+          throw new Error(
+                  "Implementation Error - cannot import existing vamsas document into an existing session, Yet!");
+        }
+        try
+        {
+          vclient = clientfactory.openAsNewSessionIClient(app,
+                  vamsasDocument);
+        } catch (InvalidSessionDocumentException e)
+        {
+          JOptionPane
+                  .showInternalMessageDialog(
+                          Desktop.desktop,
+
+                          "VAMSAS Document could not be opened as a new session - please choose another",
+                          "VAMSAS Document Import Failed",
+                          JOptionPane.ERROR_MESSAGE);
+
+        }
       }
       else
       {
-        vclient = clientfactory.getIClient(app,sess);
+        // join existing or create a new session
+        if (sess == null)
+        {
+          vclient = clientfactory.getNewSessionIClient(app);
+        }
+        else
+        {
+          vclient = clientfactory.getIClient(app, sess);
+        }
       }
-      
+      // set some properties for our VAMSAS interaction
+      setVclientConfig();
       user = vclient.getUserHandle();
-      
-    } catch (NoDefaultSessionException e)
+
+    } catch (Exception e)
     {
+      jalview.bin.Cache.log
+              .error("Couldn't instantiate vamsas client !", e);
       return false;
     }
+    return true;
+  }
+
+  private void setVclientConfig()
+  {
+    if (vclient==null)
+    {
+      return;
+    }
+    try {
+      if (vclient instanceof uk.ac.vamsas.client.simpleclient.SimpleClient)
+      {
+        uk.ac.vamsas.client.simpleclient.SimpleClientConfig cfg = ((uk.ac.vamsas.client.simpleclient.SimpleClient)  vclient).getSimpleClientConfig();
+        cfg._validatemergedroots = false;
+        cfg._validateupdatedroots= true; //we may write rubbish otherwise. 
+      }
+    }
+    catch (Error e)
+    {
+      Cache.log.warn("Probable SERIOUS VAMSAS client incompatibility - carrying on regardless",e);
+    }
     catch (Exception e)
     {
-      jalview.bin.Cache.log.error("Couldn't instantiate vamsas client !",e);
-      return false;
+      Cache.log.warn("Probable VAMSAS client incompatibility - carrying on regardless",e);
     }
-    return true;
   }
+
+  /**
+   * make the appHandle for Jalview
+   * 
+   * @return
+   */
+  private ClientHandle getJalviewHandle()
+  {
+    return new ClientHandle("jalview.bin.Jalview", jalview.bin.Cache
+            .getProperty("VERSION"));
+  }
+
   /**
    * 
    * @return true if we are registered in a vamsas session
    */
   public boolean inSession()
   {
-    return (vclient!=null);
+    return (vclient != null);
   }
+
   /**
-   * called to connect to session
-   * inits handlers, does an initial document update.
+   * called to connect to session inits handlers, does an initial document
+   * update.
    */
   public void initial_update()
   {
     if (!inSession())
     {
-      throw new Error("Impementation error! Vamsas Operations when client not initialised and connected.");
+      throw new Error(
+              "Impementation error! Vamsas Operations when client not initialised and connected.");
     }
     addDocumentUpdateHandler();
+    addStoreDocumentHandler();
     startSession();
-    Cache.log.debug("Jalview loading the Vamsas Session for the first time.");
-    dealWithDocumentUpdate();
+    Cache.log
+            .debug("Jalview loading the Vamsas Session for the first time.");
+    dealWithDocumentUpdate(false); // we don't push an update out to the
+                                    // document yet.
     Cache.log.debug("... finished update for the first time.");
   }
+
   /**
-   * Update all windows after a vamsas datamodel change.
-   * this could go on the desktop object!
+   * Update all windows after a vamsas datamodel change. this could go on the
+   * desktop object!
    * 
    */
-  protected void updateJalviewGui() 
+  protected void updateJalviewGui()
   {
     JInternalFrame[] frames = jdesktop.getAllFrames();
 
@@ -135,7 +243,7 @@ public class VamsasApplication
 
     try
     {
-      //REVERSE ORDER
+      // REVERSE ORDER
       for (int i = frames.length - 1; i > -1; i--)
       {
         if (frames[i] instanceof AlignFrame)
@@ -144,37 +252,38 @@ public class VamsasApplication
           af.alignPanel.alignmentChanged();
         }
       }
-    }
-    catch (Exception e)
+    } catch (Exception e)
     {
-      Cache.log.warn(
-          "Exception whilst refreshing jalview windows after a vamsas document update.",
-          e);
+      Cache.log
+              .warn(
+                      "Exception whilst refreshing jalview windows after a vamsas document update.",
+                      e);
     }
   }
+
   public void push_update()
   {
-    Cache.log.info("Jalview updating to the Vamsas Session.");
-    dealWithDocumentUpdate();
-    /*
-    IClientDocument cdoc=null;
-    try
-    {
-      cdoc = vclient.getClientDocument();
-    }
-    catch (Exception e)
-    {
-      Cache.log.error("Failed to get client document for update.");
-      // RAISE A WARNING DIALOG
-      disableGui(false);
-      return;
-    }
-    updateVamsasDocument(cdoc);
-    updateJalviewGui();
-    cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags back
-    vclient.updateDocument(cdoc);
-    */
-    Cache.log.info("Jalview finished updating to the Vamsas Session.");
+    Thread udthread = new Thread(new Runnable() {
+
+      public void run()
+      {
+        Cache.log.info("Jalview updating to the Vamsas Session.");
+    
+        dealWithDocumentUpdate(true);
+        /*
+         * IClientDocument cdoc=null; try { cdoc = vclient.getClientDocument(); }
+         * catch (Exception e) { Cache.log.error("Failed to get client document for
+         * update."); // RAISE A WARNING DIALOG disableGui(false); return; }
+         * updateVamsasDocument(cdoc); updateJalviewGui();
+         * cdoc.setVamsasRoots(cdoc.getVamsasRoots()); // propagate update flags
+         * back vclient.updateDocument(cdoc);
+         */
+        Cache.log.info("Jalview finished updating to the Vamsas Session.");
+        // TODO Auto-generated method stub
+      }
+      
+    });
+    udthread.start();
   }
 
   public void end_session()
@@ -184,21 +293,25 @@ public class VamsasApplication
     Cache.log.info("Jalview disconnecting from the Vamsas Session.");
     try
     {
-      if (joinedSession) {
+      if (joinedSession)
+      {
         vclient.finalizeClient();
         Cache.log.info("Jalview has left the session.");
-      } else {
-        Cache.log.warn("JV Client leaving a session that's its not joined yet.");
       }
-      joinedSession=false;
+      else
+      {
+        Cache.log
+                .warn("JV Client leaving a session that's its not joined yet.");
+      }
+      joinedSession = false;
       vclient = null;
-      app=null; user = null;
+      app = null;
+      user = null;
       jv2vobj = null;
       vobj2jv = null;
-    }
-    catch (Exception e)
+    } catch (Exception e)
     {
-      Cache.log.error("Vamsas Session finalization threw exceptions!",e);
+      Cache.log.error("Vamsas Session finalization threw exceptions!", e);
     }
   }
 
@@ -207,17 +320,19 @@ public class VamsasApplication
     Cache.log.debug("Jalview updating from sesion document ..");
     ensureJvVamsas();
     VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj,
-                                              baseProvEntry());
+            baseProvEntry(), alRedoState);
     vds.updateToJalview();
     Cache.log.debug(".. finished updating from sesion document.");
-    
+
   }
+
   private void ensureJvVamsas()
   {
     if (jv2vobj == null)
     {
       jv2vobj = new IdentityHashMap();
       vobj2jv = new Hashtable();
+      alRedoState = new Hashtable();
     }
   }
 
@@ -225,12 +340,14 @@ public class VamsasApplication
    * jalview object binding to VorbaIds
    */
   IdentityHashMap jv2vobj = null;
+
   Hashtable vobj2jv = null;
+  Hashtable alRedoState = null;
   public void updateVamsasDocument(IClientDocument doc)
   {
     ensureJvVamsas();
     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
-                                              baseProvEntry());
+            baseProvEntry(), alRedoState);
     // wander through frames
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
@@ -241,7 +358,7 @@ public class VamsasApplication
 
     try
     {
-      //REVERSE ORDER
+      // REVERSE ORDER
       for (int i = frames.length - 1; i > -1; i--)
       {
         if (frames[i] instanceof AlignFrame)
@@ -252,8 +369,18 @@ public class VamsasApplication
           vds.storeVAMSAS(af.getViewport(), af.getTitle());
         }
       }
-    }
-    catch (Exception e)
+      // REVERSE ORDER
+      for (int i = frames.length - 1; i > -1; i--)
+      {
+        if (frames[i] instanceof AlignFrame)
+        {
+          AlignFrame af = (AlignFrame) frames[i];
+
+          // add any AlignedCodonFrame mappings on this alignment to any other.
+          vds.storeSequenceMappings(af.getViewport(), af.getTitle());
+        }
+      }
+    } catch (Exception e)
     {
       Cache.log.error("Vamsas Document store exception", e);
     }
@@ -264,59 +391,208 @@ public class VamsasApplication
     uk.ac.vamsas.objects.core.Entry pentry = new uk.ac.vamsas.objects.core.Entry();
     pentry.setUser(user.getFullName());
     pentry.setApp(app.getClientUrn());
-    pentry.setDate(new org.exolab.castor.types.Date(new java.util.Date()));
+    pentry.setDate(new java.util.Date());
     pentry.setAction("created");
     return pentry;
   }
-  protected void dealWithDocumentUpdate()
+
+  /**
+   * do a vamsas document update or update jalview from the vamsas document
+   * 
+   * @param fromJalview
+   *                true to update from jalview to the vamsas document
+   */
+  protected void dealWithDocumentUpdate(boolean fromJalview)
   {
     // called by update handler for document update.
     Cache.log.debug("Updating jalview from changed vamsas document.");
     disableGui(true);
-    try {
+    try
+    {
+      long time = System.currentTimeMillis();
       IClientDocument cdoc = vclient.getClientDocument();
-      updateJalview(cdoc);
-      // cdoc.setVamsasRoots(cdoc.getVamsasRoots());
+      if (Cache.log.isDebugEnabled())
+      {
+        Cache.log.debug("Time taken to get ClientDocument = "
+                + (System.currentTimeMillis() - time));
+        time = System.currentTimeMillis();
+      }
+      if (fromJalview)
+      {
+        this.updateVamsasDocument(cdoc);
+        if (Cache.log.isDebugEnabled())
+        {
+          Cache.log
+                  .debug("Time taken to update Vamsas Document from jalview\t= "
+                          + (System.currentTimeMillis() - time));
+          time = System.currentTimeMillis();
+        }
+        cdoc.setVamsasRoots(cdoc.getVamsasRoots());
+        if (Cache.log.isDebugEnabled())
+        {
+          Cache.log.debug("Time taken to set Document Roots\t\t= "
+                  + (System.currentTimeMillis() - time));
+          time = System.currentTimeMillis();
+        }
+      }
+      else
+      {
+        updateJalview(cdoc);
+        if (Cache.log.isDebugEnabled())
+        {
+          Cache.log
+                  .debug("Time taken to update Jalview from vamsas document Roots\t= "
+                          + (System.currentTimeMillis() - time));
+          time = System.currentTimeMillis();
+        }
+
+      }
       vclient.updateDocument(cdoc);
-      cdoc=null;
-    } catch (Exception ee) {
+      if (Cache.log.isDebugEnabled())
+      {
+        Cache.log.debug("Time taken to update Session Document\t= "
+                + (System.currentTimeMillis() - time));
+        time = System.currentTimeMillis();
+      }
+      cdoc = null;
+    } catch (Exception ee)
+    {
       System.err.println("Exception whilst updating :");
       ee.printStackTrace(System.err);
     }
     Cache.log.debug("Finished updating from document change.");
     disableGui(false);
   }
+
   private void addDocumentUpdateHandler()
   {
     final VamsasApplication client = this;
-    vclient.addDocumentUpdateHandler(new PropertyChangeListener() {
+    vclient.addDocumentUpdateHandler(new PropertyChangeListener()
+    {
       public void propertyChange(PropertyChangeEvent evt)
       {
         Cache.log.debug("Dealing with document update event.");
-        client.dealWithDocumentUpdate();
+        client.dealWithDocumentUpdate(false);
         Cache.log.debug("finished dealing with event.");
       }
     });
     Cache.log.debug("Added Jalview handler for vamsas document updates.");
   }
+
+  private void addStoreDocumentHandler()
+  {
+    final VamsasApplication client = this;
+    vclient.addVorbaEventHandler(uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE, new PropertyChangeListener()
+    {
+      public void propertyChange(PropertyChangeEvent evt)
+      {
+        Cache.log.debug("Asking user if the vamsas session should be stored.");
+        int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+                "The current VAMSAS session has unsaved data - do you want to save it ?",
+            "VAMSAS Session Shutdown",
+            JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+        
+            if(reply==JOptionPane.YES_OPTION)
+            {
+              Cache.log.debug("Prompting for vamsas store filename.");
+              Desktop.instance.vamsasSave_actionPerformed(null);
+              Cache.log.debug("Finished attempt at storing document.");
+            } 
+            Cache.log.debug("finished dealing with REQUESTTOCLOSE event.");
+      }
+    });
+    Cache.log.debug("Added Jalview handler for vamsas document updates.");
+  }
   public void disableGui(boolean b)
   {
     Desktop.instance.setVamsasUpdate(b);
   }
-  private boolean joinedSession=false;
+
+  private boolean joinedSession = false;
+
+  private VamsasListener picker = null;
+
   private void startSession()
   {
     if (inSession())
     {
-      try {
+      try
+      {
         vclient.joinSession();
-        joinedSession=true;
-      }
-      catch (Exception e)
+        joinedSession = true;
+      } catch (Exception e)
       {
         // Complain to GUI
-        Cache.log.error("Failed to join vamsas session.",e);
-        vclient=null;
+        Cache.log.error("Failed to join vamsas session.", e);
+        vclient = null;
+      }
+      try
+      {
+        final IPickManager pm = vclient.getPickManager();
+        final StructureSelectionManager ssm = StructureSelectionManager
+                .getStructureSelectionManager();
+        pm.registerMessageHandler(new IMessageHandler()
+        {
+          String last = null;
+
+          public void handleMessage(Message message)
+          {
+            if (message instanceof MouseOverMessage && vobj2jv != null)
+            {
+              MouseOverMessage mm = (MouseOverMessage) message;
+              String mstring = mm.getVorbaID() + " " + mm.getPosition();
+              if (last != null && mstring.equals(last))
+              {
+                return;
+              }
+              // if (Cache.log.isDebugEnabled())
+              // {
+              // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+"
+              // "+mm.getPosition());
+              // }
+              Object jvobj = vobj2jv.get(mm.getVorbaID());
+              if (jvobj != null && jvobj instanceof SequenceI)
+              {
+                last = mstring;
+                // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+"
+                // bound to "+jvobj+" at "+mm.getPosition());
+                // position is in sequence or in aligned sequence ???????
+                ssm.mouseOverVamsasSequence((SequenceI) jvobj, mm
+                        .getPosition());
+              }
+            }
+          }
+        });
+        picker = new VamsasListener()
+        {
+          SequenceI last = null;
+
+          int i = -1;
+
+          public void mouseOver(SequenceI seq, int index)
+          {
+            if (jv2vobj == null)
+              return;
+            if (seq != last || i != index)
+            {
+              VorbaId v = (VorbaId) jv2vobj.get(seq);
+              if (v != null)
+              {
+                Cache.log.debug("Mouse over " + v.getId() + " bound to "
+                        + seq + " at " + index);
+                last = seq;
+                i = index;
+                MouseOverMessage message = new MouseOverMessage(v.getId(),
+                        index);
+                pm.sendMessage(message);
+              }
+            }
+          }
+        };
+        ssm.addStructureViewerListener(picker); // better method here
+      } catch (Exception e)
+      {
+        Cache.log.error("Failed to init Vamsas Picking", e);
       }
     }
   }