new viewport IDs and jalview XML storage/sync in vamsas appdata and refactored more...
authorjprocter <Jim Procter>
Wed, 12 Nov 2008 17:05:08 +0000 (17:05 +0000)
committerjprocter <Jim Procter>
Wed, 12 Nov 2008 17:05:08 +0000 (17:05 +0000)
src/jalview/io/VamsasAppDatastore.java

index c2031cc..34219b4 100644 (file)
@@ -19,6 +19,7 @@
 package jalview.io;
 
 import jalview.bin.Cache;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
@@ -30,17 +31,24 @@ import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.Desktop;
 import jalview.gui.TreePanel;
+import jalview.io.vamsas.Datasetsequence;
 import jalview.io.vamsas.DatastoreItem;
 import jalview.io.vamsas.Rangetype;
+import jalview.util.UrlLink;
 
+import java.io.IOException;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.Vector;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
 
 import uk.ac.vamsas.client.*;
 import uk.ac.vamsas.objects.core.*;
+import uk.ac.vamsas.objects.utils.Properties;
 
 /*
  * 
@@ -71,14 +79,30 @@ public class VamsasAppDatastore
 
   private static final String THRESHOLD = "threshold";
 
+  /**
+   * template for provenance entries written to vamsas session document
+   */
   Entry provEntry = null;
 
+  /**
+   * Instance of the session document being synchronized with
+   */
   IClientDocument cdoc;
 
+  /**
+   * map Vorba (vamsas object xml ref) IDs to live jalview object references
+   */
   Hashtable vobj2jv;
 
+  /**
+   * map live jalview object references to Vorba IDs
+   */
   IdentityHashMap jv2vobj;
 
+  /**
+   * map jalview sequence set ID (which is vorba ID for alignment) to last
+   * recorded hash value for the alignment viewport (the undo/redo hash value)
+   */
   Hashtable alignRDHash;
 
   public VamsasAppDatastore(IClientDocument cdoc, Hashtable vobj2jv,
@@ -89,6 +113,23 @@ public class VamsasAppDatastore
     this.jv2vobj = jv2vobj;
     this.provEntry = provEntry;
     this.alignRDHash = alignRDHash;
+    buildSkipList();
+  }
+
+  /**
+   * the skipList used to skip over views from Jalview Appdata's that we've
+   * already syncrhonized
+   */
+  Hashtable skipList;
+
+  private void buildSkipList()
+  {
+    skipList = new Hashtable();
+    AlignFrame[] al = Desktop.getAlignframes();
+    for (int f = 0; al != null && f < al.length; f++)
+    {
+      skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
+    }
   }
 
   /**
@@ -100,6 +141,36 @@ public class VamsasAppDatastore
     {
       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
     }
+    // check if we're working with a string - then workaround 
+    // the use of IdentityHashTable because different strings
+    // have different object IDs.
+    if (jvobj instanceof String)
+    { 
+      Object seqsetidobj = null;
+      seqsetidobj = getVamsasObjectBinding().get(jvobj);
+      if (seqsetidobj != null)
+      {
+        if (seqsetidobj instanceof String)
+        {
+          // what is expected. object returned by av.getSequenceSetId() -
+          // reverse lookup to get the 'registered' instance of this string
+          Vobject obj = getjv2vObj(seqsetidobj);
+          if (obj!=null && !(obj instanceof Alignment))
+          {
+            Cache.log.warn("IMPLEMENTATION ERROR?: Unexpected mapping for unmapped jalview string object content:"
+                    + seqsetidobj + " to object " + obj);
+          }
+          return obj;
+        }
+        else
+        {
+          Cache.log.warn("Unexpected mapping for Jalview String Object ID "
+                  + seqsetidobj
+                  + " to another jalview dataset object " + seqsetidobj);
+        }
+      }
+    }
+
     if (Cache.log.isDebugEnabled())
     {
       Cache.log.debug("Returning null VorbaID binding for jalview object "
@@ -181,8 +252,9 @@ public class VamsasAppDatastore
    *                alignViewport to be stored
    * @param aFtitle
    *                title for alignment
+   * @return true if alignment associated with viewport was stored/synchronized to document
    */
-  public void storeVAMSAS(AlignViewport av, String aFtitle)
+  public boolean storeVAMSAS(AlignViewport av, String aFtitle)
   {
     try
     {
@@ -197,11 +269,22 @@ public class VamsasAppDatastore
         Cache.log.warn("Creating new dataset for an alignment.");
         jal.setDataset(null);
       }
-      dataset = (DataSet) getjv2vObj(jal.getDataset());
+      // try and get alignment and association for sequence set id
+
+      Alignment alignment = (Alignment) getjv2vObj(av.getSequenceSetId());
+      if (alignment!=null)
+      {
+        dataset = (DataSet) alignment.getV_parent();
+      } else {
+       // is the dataset already registered
+       dataset = (DataSet) getjv2vObj(jal.getDataset());
+      }
+      
       if (dataset == null)
       {
         // it might be that one of the dataset sequences does actually have a
-        // binding, so search for it indirectly.
+        // binding, so search for it indirectly. If it does, then the local jalview dataset
+        // must be merged with the existing vamsas dataset.
         jalview.datamodel.SequenceI[] jdatset = jal.getDataset()
                 .getSequencesArray();
         for (int i = 0; i < jdatset.length; i++)
@@ -217,10 +300,10 @@ public class VamsasAppDatastore
               }
               else
               {
-                if (dataset != vbound.getV_parent())
+                if (vbound.getV_parent()!=null && dataset != vbound.getV_parent())
                 {
                   throw new Error(
-                          "IMPLEMENTATION ERROR: Cannot map an alignment of sequences from datasets into the vamsas document.");
+                          "IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.");
                   // This occurs because the dataset for the alignment we are
                   // trying to
                 }
@@ -232,6 +315,8 @@ public class VamsasAppDatastore
 
       if (dataset == null)
       {
+        Cache.log.warn("Creating new vamsas dataset for alignment view "
+                + av.getSequenceSetId());
         // we create a new dataset on the default vamsas root.
         root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying.
         dataset = new DataSet();
@@ -261,107 +346,11 @@ public class VamsasAppDatastore
         // referenced
         // sequences
         // to dataset.
-        sequence = (Sequence) getjv2vObj(sq);
-        if (sequence == null)
-        {
-          sequence = new Sequence();
-          bindjvvobj(sq, sequence);
-          sq.setVamsasId(sequence.getVorbaId().getId());
-          sequence.setSequence(sq.getSequenceAsString());
-          sequence.setDictionary(dict);
-          sequence.setName(sq.getName());
-          sequence.setStart(sq.getStart());
-          sequence.setEnd(sq.getEnd());
-          sequence.setDescription(sq.getDescription());
-          dataset.addSequence(sequence);
-          dssmods.addElement(dssmods);
-        }
-        else
-        {
-          boolean dsmod = false;
-          // verify and update principal attributes.
-          if (sq.getDescription() != null
-                  && (sequence.getDescription() == null || !sequence
-                          .getDescription().equals(sq.getDescription())))
-          {
-            sequence.setDescription(sq.getDescription());
-            dsmod = true;
-          }
-          if (sequence.getSequence() == null
-                  || !sequence.getSequence().equals(
-                          sq.getSequenceAsString()))
-          {
-            if (sequence.getStart() != sq.getStart()
-                    || sequence.getEnd() != sq.getEnd())
-            {
-              // update modified sequence.
-              sequence.setSequence(sq.getSequenceAsString());
-              sequence.setStart(sq.getStart());
-              sequence.setEnd(sq.getEnd());
-              dsmod = true;
-            }
-          }
-          if (!dict.equals(sequence.getDictionary()))
-          {
-            sequence.setDictionary(dict);
-            dsmod = true;
-          }
-          if (!sequence.getName().equals(sq.getName()))
-          {
-            sequence.setName(sq.getName());
-            dsmod = true;
-          }
-          if (dsmod)
-          {
-            dssmods.addElement(sequence);
-          }
-        }
-        // add or update any new features/references on dataset sequence
-        if (sq.getSequenceFeatures() != null)
-        {
-          int sfSize = sq.getSequenceFeatures().length;
-
-          for (int sf = 0; sf < sfSize; sf++)
-          {
-            // TODO: update/modifiable synchronizer
-            jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) sq
-                    .getSequenceFeatures()[sf];
-
-            DataSetAnnotations dsa = (DataSetAnnotations) getjv2vObj(feature);
-            if (dsa == null)
-            {
-              dsa = (DataSetAnnotations) getDSAnnotationFromJalview(
-                      new DataSetAnnotations(), feature);
-              if (dsa.getProvenance() == null)
-              {
-                dsa.setProvenance(new Provenance());
-              }
-              addProvenance(dsa.getProvenance(), "created"); // JBPNote - need
-              // to update
-              dsa.addSeqRef(sequence); // we have just created this annotation
-              // - so safe to use this
-              bindjvvobj(feature, dsa);
-              dataset.addDataSetAnnotations(dsa);
-            }
-            else
-            {
-              // todo: verify and update dataset annotations for sequence
-              System.out.println("update dataset sequence annotations.");
-            }
-          }
-        }
-        if (sq.getDatasetSequence() == null && sq.getDBRef() != null)
-        {
-          // only sync database references for dataset sequences
-          DBRefEntry[] entries = sq.getDBRef();
-          jalview.datamodel.DBRefEntry dbentry;
-          for (int db = 0; db < entries.length; db++)
-          {
-            Rangetype dbr = new jalview.io.vamsas.Dbref(this,
-                    dbentry = entries[db], sq, sequence);
-          }
-
-        }
+        Datasetsequence dssync = new jalview.io.vamsas.Datasetsequence(this, sq, dict, dataset);
+        sequence = (Sequence) dssync.getVobj();
+        if (dssync.getModified()) { 
+          dssmods.addElement(sequence); 
+        };
       }
       if (dssmods.size() > 0)
       {
@@ -377,23 +366,12 @@ public class VamsasAppDatastore
       // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance()));
       // ////////////////////////////////////////////
       if (!av.getAlignment().isAligned())
-        return; // TODO: trees could be written - but for the moment we just
-      // skip
-      // ////////////////////////////////////////////
-      // Save the Alignments
-
-      Alignment alignment = (Alignment) getjv2vObj(av.getSequenceSetId()); // bind
-      // to
-      // the
-      // value
-      // used
-      // to
-      // associate
-      // different
-      // views
-      // to
-      // same
-      // alignment
+      {
+        // TODO: trees could be written - but for the moment we just
+        addToSkipList(av);
+        // add to the JalviewXML skipList and ..
+        return false;
+      }
 
       if (alignment == null)
       {
@@ -659,12 +637,12 @@ public class VamsasAppDatastore
                 an.setGroup(Integer.toString(aa[i].graphGroup));
                 if (aa[i].threshold != null && aa[i].threshold.displayed)
                 {
-                  an.addProperty(newProperty(THRESHOLD, "float", ""
+                  an.addProperty(Properties.newProperty(THRESHOLD, Properties.FLOATTYPE, ""
                           + aa[i].threshold.value));
                   if (aa[i].threshold.label != null)
                   {
-                    an.addProperty(newProperty(THRESHOLD + "Name",
-                            "string", "" + aa[i].threshold.label));
+                    an.addProperty(Properties.newProperty(THRESHOLD + "Name", Properties.STRINGTYPE,
+                             "" + aa[i].threshold.label));
                   }
                 }
               }
@@ -722,8 +700,18 @@ public class VamsasAppDatastore
     catch (Exception ex)
     {
       ex.printStackTrace();
+      return false;
     }
+    return true;
+  }
 
+  private void addToSkipList(AlignViewport av)
+  {
+    if (skipList == null)
+    {
+      skipList = new Hashtable();
+    }
+    skipList.put(av.getSequenceSetId(), av);
   }
 
   /**
@@ -894,8 +882,7 @@ public class VamsasAppDatastore
         modal = true;
         alseq.setName(valseq.getName());
       }
-      if (alseq.getDescription() == null
-              || (valseq.getDescription() == null || alseq.getDescription()
+      if (alseq.getDescription()==null || (valseq.getDescription() != null && !alseq.getDescription()
                       .equals(valseq.getDescription())))
       {
         alseq.setDescription(valseq.getDescription());
@@ -912,11 +899,21 @@ public class VamsasAppDatastore
       alseq = new jalview.datamodel.Sequence(valseq.getName(), valseq
               .getSequence().replace(valGapchar, gapChar), (int) valseq
               .getStart(), (int) valseq.getEnd());
-
+      
       Vobject datsetseq = (Vobject) valseq.getRefid();
       if (datsetseq != null)
       {
         alseq.setDatasetSequence((SequenceI) getvObj2jv(datsetseq)); // exceptions
+        if (valseq.getDescription()!=null)
+        {
+          alseq.setDescription(valseq.getDescription());
+        } else {
+          // inherit description line from dataset.
+          if (alseq.getDatasetSequence().getDescription()!=null)
+          {
+            alseq.setDescription(alseq.getDatasetSequence().getDescription());
+          }
+        }
         // if
         // AlignemntSequence
         // reference
@@ -1037,10 +1034,11 @@ public class VamsasAppDatastore
       // bits.
       if (alan.getThreshold() != null && alan.getThreshold().displayed)
       {
-        an.addProperty(newProperty(THRESHOLD, "float", ""
+        an.addProperty(Properties.newProperty(THRESHOLD, Properties.FLOATTYPE,
+                 ""
                 + alan.getThreshold().value));
         if (alan.getThreshold().label != null)
-          an.addProperty(newProperty(THRESHOLD + "Name", "string", ""
+          an.addProperty(Properties.newProperty(THRESHOLD + "Name", Properties.STRINGTYPE, ""
                   + alan.getThreshold().label));
       }
       ((DataSet) sref.getV_parent()).addDataSetAnnotations(an);
@@ -1167,78 +1165,16 @@ public class VamsasAppDatastore
     switch (alan.graph)
     {
     case AlignmentAnnotation.BAR_GRAPH:
-      an.addProperty(newProperty(DISCRETE_ANNOTATION, "boolean", "true"));
+      an.addProperty(Properties.newProperty(DISCRETE_ANNOTATION, Properties.BOOLEANTYPE, "true"));
       break;
     case AlignmentAnnotation.LINE_GRAPH:
-      an.addProperty(newProperty(CONTINUOUS_ANNOTATION, "boolean", "true"));
+      an.addProperty(Properties.newProperty(CONTINUOUS_ANNOTATION, Properties.BOOLEANTYPE, "true"));
       break;
     default:
       // don't add any kind of discrete or continous property info.
     }
   }
 
-  private Property newProperty(String name, String type, String content)
-  {
-    Property vProperty = new Property();
-    vProperty.setName(name);
-    if (type != null)
-    {
-      vProperty.setType(type);
-    }
-    else
-    {
-      vProperty.setType("String");
-    }
-    vProperty.setContent(content);
-    return vProperty;
-  }
-
-  /**
-   * correctly create a RangeAnnotation from a jalview sequence feature
-   * 
-   * @param dsa
-   *                (typically DataSetAnnotations or
-   *                AlignmentSequenceAnnotation)
-   * @param feature
-   *                (the feature to be mapped from)
-   * @return
-   */
-  private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa,
-          SequenceFeature feature)
-  {
-    dsa.setType(feature.getType());
-    Seg vSeg = new Seg();
-    vSeg.setStart(feature.getBegin());
-    vSeg.setEnd(feature.getEnd());
-    vSeg.setInclusive(true);
-    dsa.addSeg(vSeg);
-    dsa.setDescription(feature.getDescription());
-    dsa.setStatus(feature.getStatus());
-    if (feature.links != null && feature.links.size() > 0)
-    {
-      for (int i = 0, iSize = feature.links.size(); i < iSize; i++)
-      {
-        String link = (String) feature.links.elementAt(i);
-        int sep = link.indexOf('|');
-        if (sep > -1)
-        {
-          Link vLink = new Link();
-          if (sep > 0)
-          {
-            vLink.setContent(link.substring(0, sep - 1));
-          }
-          else
-          {
-            vLink.setContent("");
-          }
-          vLink.setHref(link.substring(sep + 1)); // TODO: validate href.
-          dsa.addLink(vLink);
-        }
-      }
-    }
-    dsa.setGroup(feature.getFeatureGroup());
-    return dsa;
-  }
 
   /**
    * get start<end range of segment, adjusting for inclusivity flag and
@@ -1276,39 +1212,313 @@ public class VamsasAppDatastore
    */
   private boolean isJalviewOnly(AlignmentAnnotation annotation)
   {
-    return annotation.label.equals("Quality")
+    return annotation.autoCalculated || annotation.label.equals("Quality")
             || annotation.label.equals("Conservation")
             || annotation.label.equals("Consensus");
   }
 
+  boolean dojvsync = true;
+
+  // boolean dojvsync = false; // disables Jalview AppData IO
   /**
-   * This will return the first AlignFrame viewing AlignViewport av. It will
-   * break if there are more than one AlignFrames viewing a particular av. This
-   * also shouldn't be in the io package.
-   * 
-   * @param av
-   * @return alignFrame for av
+   * list of alignment views created when updating Jalview from document.
    */
-  public AlignFrame getAlignFrameFor(AlignViewport av)
+  private Vector newAlignmentViews = new Vector();
+
+  /**
+   * update local jalview view settings from the stored appdata (if any)
+   */
+  public void updateJalviewFromAppdata()
   {
-    if (Desktop.desktop != null)
+    // recover any existing Jalview data from appdata
+    // TODO: recover any PDB files stored as attachments in the vamsas session
+    // and initialise the Jalview2XML.alreadyLoadedPDB hashtable with mappings
+    // to temp files.
     {
-      javax.swing.JInternalFrame[] frames = Desktop.instance.getAllFrames();
+      final IClientAppdata cappdata = cdoc.getClientAppdata();
+      if (cappdata != null)
+      {
+        if (cappdata.hasClientAppdata())
+        {
+          // TODO: how to check version of Jalview client app data and whether
+          // it has been modified
+          // client data is shared over all app clients
+          try
+          {
+            jalview.gui.Jalview2XML fromxml = new jalview.gui.Jalview2XML();
+            fromxml.attemptversion1parse = false;
+            fromxml.setUniqueSetSuffix("");
+            fromxml.setObjectMappingTables(vobj2jv, jv2vobj); // mapKeysToString
+            // and
+            // mapValuesToString
+            fromxml.setSkipList(skipList);
+            jalview.util.jarInputStreamProvider jprovider = new jalview.util.jarInputStreamProvider()
+            {
+
+              public String getFilename()
+              {
+
+                // TODO Get the vamsas session ID here
+                return "Jalview Vamsas Document Client Data";
+              }
+
+              public JarInputStream getJarInputStream() throws IOException
+              {
+                jalview.bin.Cache.log
+                        .debug("Returning client input stream for Jalview from Vamsas Document.");
+                return new JarInputStream(cappdata.getClientInputStream());
+              }
+            };
+            if (dojvsync)
+            {
+              fromxml.LoadJalviewAlign(jprovider);
+            }
+          } catch (Exception e)
+          {
+
+          } catch (OutOfMemoryError e)
+          {
+
+          } catch (Error e)
+          {
+
+          }
+        }
+      }
+      if (cappdata.hasUserAppdata())
+      {
+        // TODO: how to check version of Jalview user app data and whether it
+        // has been modified
+        // user data overrides data shared over all app clients ?
+        try
+        {
+          jalview.gui.Jalview2XML fromxml = new jalview.gui.Jalview2XML();
+          fromxml.attemptversion1parse = false;
+          fromxml.setUniqueSetSuffix("");
+          fromxml.setSkipList(skipList);
+          fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
+                  mapValuesToString(jv2vobj));
+          jalview.util.jarInputStreamProvider jarstream = new jalview.util.jarInputStreamProvider()
+          {
+
+            public String getFilename()
+            {
+
+              // TODO Get the vamsas session ID here
+              return "Jalview Vamsas Document User Data";
+            }
+
+            public JarInputStream getJarInputStream() throws IOException
+            {
+              jalview.bin.Cache.log
+                      .debug("Returning user input stream for Jalview from Vamsas Document.");
+              return new JarInputStream(cappdata.getUserInputStream());
+            }
+          };
+          if (dojvsync)
+          {
+            fromxml.LoadJalviewAlign(jarstream);
+          }
+        } catch (Exception e)
+        {
+
+        } catch (OutOfMemoryError e)
+        {
 
-      for (int t = 0; t < frames.length; t++)
+        } catch (Error e)
+        {
+
+        }
+      }
+
+    }
+    flushAlignViewports();
+  }
+
+  /**
+   * remove any spurious views generated by document synchronization
+   */
+  private void flushAlignViewports()
+  {
+    // remove any additional viewports originally recovered from the vamsas
+    // document.
+    // search for all alignframes containing viewports generated from document
+    // sync,
+    // and if any contain more than one view, then remove the one generated by
+    // document update.
+    AlignViewport views[], av = null;
+    AlignFrame af = null;
+    Iterator newviews = newAlignmentViews.iterator();
+    while (newviews.hasNext())
+    {
+      av = (AlignViewport) newviews.next();
+      af = Desktop.getAlignFrameFor(av);
+      // TODO implement this : af.getNumberOfViews
+      String seqsetidobj = av.getSequenceSetId();
+      views = Desktop.getViewports(seqsetidobj);
+      Cache.log.debug("Found "
+              + (views == null ? " no " : "" + views.length)
+              + " views for '" + av.getSequenceSetId() + "'");
+      if (views.length > 1)
       {
-        if (frames[t] instanceof AlignFrame)
+        // we need to close the original document view.
+
+        // work out how to do this by seeing if the views are gathered.
+        // pretty clunky but the only way to do this without adding more flags
+        // to the align frames.
+        boolean gathered = false;
+        String newviewid = null;
+        AlignedCodonFrame[] mappings = av.getAlignment().getCodonFrames();
+        for (int i = 0; i < views.length; i++)
+        {
+          if (views[i] != av)
+          {
+            AlignFrame viewframe = Desktop.getAlignFrameFor(views[i]);
+            if (viewframe == af)
+            {
+              gathered = true;
+            }
+            newviewid = views[i].getSequenceSetId();
+          }
+          else
+          {
+            // lose the reference to the vamsas document created view
+            views[i] = null;
+          }
+        }
+        // close the view generated by the vamsas document synchronization
+        if (gathered)
         {
-          if (((AlignFrame) frames[t]).getViewport() == av)
+          af.closeView(av);
+        }
+        else
+        {
+          af.closeMenuItem_actionPerformed(false);
+        }
+        replaceJvObjMapping(seqsetidobj, newviewid);
+        seqsetidobj = newviewid;
+        // not sure if we need to do this:
+
+        if (false) // mappings != null)
+        {
+          // ensure sequence mappings from vamsas document view still
+          // active
+          if (mappings != null && mappings.length > 0)
           {
-            return (AlignFrame) frames[t];
+            jalview.structure.StructureSelectionManager
+                    .getStructureSelectionManager().addMappings(mappings);
           }
         }
       }
+      // ensure vamsas object binds to the stored views retrieved from
+      // Jalview appdata
+      //jalview.structure.StructureSelectionManager
+      //       .getStructureSelectionManager()
+      //      .addStructureViewerListener(viewframe.alignPanel);
+
+    }
+
+    newviews = null;
+    newAlignmentViews.clear();
+  }
+
+  /**
+   * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
+   * binding tables
+   * 
+   * @param oldjvobject
+   * @param newjvobject (may be null)
+   */
+  private void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
+  {
+    Object vobject = jv2vobj.remove(oldjvobject);
+    if (vobject == null)
+    {
+      throw new Error(
+              "IMPLEMENTATION ERROR: old jalview object is not bound ! ("
+                      + oldjvobject + ")");
+    }
+    if (newjvobject!=null)
+    {
+      jv2vobj.put(newjvobject, vobject);
+      vobj2jv.put(vobject, newjvobject);
+    }
+  }
+
+  /**
+   * Update the jalview client and user appdata from the local jalview settings
+   */
+  public void updateJalviewClientAppdata()
+  {
+    final IClientAppdata cappdata = cdoc.getClientAppdata();
+    if (cappdata != null)
+    {
+      try
+      {
+        jalview.gui.Jalview2XML jxml = new jalview.gui.Jalview2XML();
+        jxml.setObjectMappingTables(mapKeysToString(vobj2jv),
+                mapValuesToString(jv2vobj));
+        jxml.setSkipList(skipList);
+        if (dojvsync)
+        {
+          jxml.SaveState(new JarOutputStream(cappdata
+                  .getClientOutputStream()));
+        }
+
+      } catch (Exception e)
+      {
+        // TODO raise GUI warning if user requests it.
+        jalview.bin.Cache.log
+                .error(
+                        "Couldn't update jalview client application data. Giving up - local settings probably lost.",
+                        e);
+      }
+    }
+    else
+    {
+      jalview.bin.Cache.log
+              .error("Couldn't access client application data for vamsas session. This is probably a vamsas client bug.");
     }
-    return null;
   }
 
+  /**
+   * translate the Vobject keys to strings for use in Jalview2XML
+   * 
+   * @param jv2vobj2
+   * @return
+   */
+  private IdentityHashMap mapValuesToString(IdentityHashMap jv2vobj2)
+  {
+    IdentityHashMap mapped = new IdentityHashMap();
+    Iterator keys = jv2vobj2.keySet().iterator();
+    while (keys.hasNext())
+    {
+      Object key = keys.next();
+      mapped.put(key, jv2vobj2.get(key).toString());
+    }
+    return mapped;
+  }
+
+  /**
+   * translate the Vobject values to strings for use in Jalview2XML
+   * 
+   * @param vobj2jv2
+   * @return hashtable with string values
+   */
+  private Hashtable mapKeysToString(Hashtable vobj2jv2)
+  {
+    Hashtable mapped = new Hashtable();
+    Iterator keys = vobj2jv2.keySet().iterator();
+    while (keys.hasNext())
+    {
+      Object key = keys.next();
+      mapped.put(key.toString(), vobj2jv2.get(key));
+    }
+    return mapped;
+  }
+  /**
+   * synchronize Jalview from the vamsas document
+   */
   public void updateToJalview()
   {
     VAMSAS _roots[] = cdoc.getVamsasRoots();
@@ -1342,28 +1552,15 @@ public class VamsasAppDatastore
         // TODO: test sequence merging - we preserve existing non vamsas
         // sequences but add in any new vamsas ones, and don't yet update any
         // sequence attributes
-        for (i = 0; i < iSize; i++)
+        for (i = 0; i < iSize
+        ; i++)
         {
           Sequence vdseq = dataset.getSequence(i);
-          jalview.datamodel.SequenceI dsseq = (SequenceI) getvObj2jv(vdseq);
-          if (dsseq != null)
+          jalview.io.vamsas.Datasetsequence dssync = new Datasetsequence(this, vdseq);
+          
+          jalview.datamodel.SequenceI dsseq = (SequenceI) dssync.getJvobj();
+          if (dssync.isAddfromdoc())
           {
-            if (!dsseq.getSequenceAsString().equals(vdseq.getSequence()))
-            {
-              throw new Error(
-                      "Broken! - mismatch of dataset sequence: and jalview internal dataset sequence.");
-            }
-            jremain--;
-          }
-          else
-          {
-            dsseq = new jalview.datamodel.Sequence(dataset.getSequence(i)
-                    .getName(), dataset.getSequence(i).getSequence(),
-                    (int) dataset.getSequence(i).getStart(), (int) dataset
-                            .getSequence(i).getEnd());
-            dsseq.setDescription(dataset.getSequence(i).getDescription());
-            bindjvvobj(dsseq, dataset.getSequence(i));
-            dsseq.setVamsasId(dataset.getSequence(i).getVorbaId().getId());
             dsseqs.add(dsseq);
           }
           if (vdseq.getDbRefCount() > 0)
@@ -1415,13 +1612,8 @@ public class VamsasAppDatastore
               {
                 if (dseta.getAnnotationElementCount() == 0)
                 {
-                  jalview.datamodel.SequenceFeature sf = (jalview.datamodel.SequenceFeature) getvObj2jv(dseta);
-                  if (sf == null)
-                  {
-                    dsSeq
-                            .addSequenceFeature(sf = getJalviewSeqFeature(dseta));
-                    bindjvvobj(sf, dseta);
-                  }
+                  new jalview.io.vamsas.Sequencefeature(this, dseta, dsSeq);
+                  
                 }
                 else
                 {
@@ -1434,6 +1626,8 @@ public class VamsasAppDatastore
                           .warn("Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
                 }
               }
+            } else {
+              Cache.log.warn("Ignoring multiply referenced dataset sequence annotation for binding to datsaet sequence features.");
             }
           }
         }
@@ -1445,12 +1639,17 @@ public class VamsasAppDatastore
           {
             uk.ac.vamsas.objects.core.Alignment alignment = dataset
                     .getAlignment(al);
+            // TODO check this handles multiple views properly
             AlignViewport av = findViewport(alignment);
 
             jalview.datamodel.AlignmentI jal = null;
             if (av != null)
             {
-              jal = av.getAlignment();
+              // TODO check that correct alignment object is retrieved when
+              // hidden seqs exist.
+              jal = (av.hasHiddenRows()) ? av.getAlignment()
+                      .getHiddenSequences().getFullAlignment() : av
+                      .getAlignment();
             }
             iSize = alignment.getAlignmentSequenceCount();
             boolean newal = (jal == null) ? true : false;
@@ -1612,8 +1811,10 @@ public class VamsasAppDatastore
               // ///////////////////////////////
               // construct alignment view
               alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH,
-                      AlignFrame.DEFAULT_HEIGHT);
+                      AlignFrame.DEFAULT_HEIGHT, alignment.getVorbaId()
+                              .toString());
               av = alignFrame.getViewport();
+              newAlignmentViews.addElement(av);
               String title = alignment.getProvenance().getEntry(
                       alignment.getProvenance().getEntryCount() - 1)
                       .getAction();
@@ -1642,8 +1843,9 @@ public class VamsasAppDatastore
             {
               // find the alignFrame for jal.
               // TODO: fix this so we retrieve the alignFrame handing av
-              // *directly*
-              alignFrame = getAlignFrameFor(av);
+              // *directly* (JBPNote - don't understand this now)
+              // TODO: make sure all associated views are refreshed
+              alignFrame = Desktop.getAlignFrameFor(av);
               if (refreshal)
               {
                 av.alignmentChanged(alignFrame.alignPanel);
@@ -1709,7 +1911,8 @@ public class VamsasAppDatastore
   public AlignViewport findViewport(Alignment alignment)
   {
     AlignViewport av = null;
-    AlignViewport[] avs = findViewportForSequenceSetId((String) getvObj2jv(alignment));
+    AlignViewport[] avs = Desktop
+            .getViewports((String) getvObj2jv(alignment));
     if (avs != null)
     {
       av = avs[0];
@@ -1717,34 +1920,6 @@ public class VamsasAppDatastore
     return av;
   }
 
-  private AlignViewport[] findViewportForSequenceSetId(String sequenceSetId)
-  {
-    Vector viewp = new Vector();
-    if (Desktop.desktop != null)
-    {
-      javax.swing.JInternalFrame[] frames = Desktop.instance.getAllFrames();
-
-      for (int t = 0; t < frames.length; t++)
-      {
-        if (frames[t] instanceof AlignFrame)
-        {
-          if (((AlignFrame) frames[t]).getViewport().getSequenceSetId()
-                  .equals(sequenceSetId))
-          {
-            viewp.addElement(((AlignFrame) frames[t]).getViewport());
-          }
-        }
-      }
-      if (viewp.size() > 0)
-      {
-        AlignViewport[] vp = new AlignViewport[viewp.size()];
-        viewp.copyInto(vp);
-        return vp;
-      }
-    }
-    return null;
-  }
-
   // bitfields - should be a template in j1.5
   private static int HASSECSTR = 0;
 
@@ -2139,6 +2314,8 @@ public class VamsasAppDatastore
         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
                 arow);
         jan.setThreshold(null);
+        jan.annotationId = annotation.getVorbaId().toString(); // keep all the
+        // ids together.
       }
       if (annotation.getLinkCount() > 0)
       {
@@ -2177,22 +2354,6 @@ public class VamsasAppDatastore
     return null;
   }
 
-  private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta)
-  {
-    int[] se = getBounds(dseta);
-    SequenceFeature sf = new jalview.datamodel.SequenceFeature(dseta
-            .getType(), dseta.getDescription(), dseta.getStatus(), se[0],
-            se[1], dseta.getGroup());
-    if (dseta.getLinkCount() > 0)
-    {
-      Link[] links = dseta.getLink();
-      for (int i = 0; i < links.length; i++)
-      {
-        sf.addLink(links[i].getContent() + "|" + links[i].getHref());
-      }
-    }
-    return sf;
-  }
 
   /**
    * get real bounds of a RangeType's specification. start and end are an
@@ -2522,4 +2683,28 @@ public class VamsasAppDatastore
               e);
     }
   }
+
+  public void clearSkipList()
+  {
+    if (skipList != null)
+    {
+      skipList.clear();
+    }
+  }
+
+  /**
+   * @return the skipList
+   */
+  public Hashtable getSkipList()
+  {
+    return skipList;
+  }
+
+  /**
+   * @param skipList the skipList to set
+   */
+  public void setSkipList(Hashtable skipList)
+  {
+    this.skipList = skipList;
+  }
 }