JAL-1683 replace year/version strings with tokens in source
[jalview.git] / src / jalview / gui / Jalview2XML.java
index 4533b25..fb53900 100644 (file)
@@ -1,44 +1,81 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
- * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
- *
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
  * This file is part of Jalview.
- *
+ * 
  * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * Jalview is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.gui;
 
-import java.awt.Rectangle;
-import java.io.*;
-import java.lang.reflect.InvocationTargetException;
-import java.net.*;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.jar.*;
-
-import javax.swing.*;
-
-import org.exolab.castor.xml.*;
-
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
-import jalview.schemabinding.version2.*;
-import jalview.schemes.*;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.schemabinding.version2.AlcodMap;
+import jalview.schemabinding.version2.Alcodon;
+import jalview.schemabinding.version2.AlcodonFrame;
+import jalview.schemabinding.version2.Annotation;
+import jalview.schemabinding.version2.AnnotationColours;
+import jalview.schemabinding.version2.AnnotationElement;
+import jalview.schemabinding.version2.CalcIdParam;
+import jalview.schemabinding.version2.DBRef;
+import jalview.schemabinding.version2.Features;
+import jalview.schemabinding.version2.Group;
+import jalview.schemabinding.version2.HiddenColumns;
+import jalview.schemabinding.version2.JGroup;
+import jalview.schemabinding.version2.JSeq;
+import jalview.schemabinding.version2.JalviewModel;
+import jalview.schemabinding.version2.JalviewModelSequence;
+import jalview.schemabinding.version2.MapListFrom;
+import jalview.schemabinding.version2.MapListTo;
+import jalview.schemabinding.version2.Mapping;
+import jalview.schemabinding.version2.MappingChoice;
+import jalview.schemabinding.version2.OtherData;
+import jalview.schemabinding.version2.PdbentryItem;
+import jalview.schemabinding.version2.Pdbids;
+import jalview.schemabinding.version2.Property;
+import jalview.schemabinding.version2.Sequence;
+import jalview.schemabinding.version2.SequenceSet;
+import jalview.schemabinding.version2.SequenceSetProperties;
+import jalview.schemabinding.version2.Setting;
+import jalview.schemabinding.version2.StructureState;
+import jalview.schemabinding.version2.ThresholdLine;
+import jalview.schemabinding.version2.Tree;
+import jalview.schemabinding.version2.UserColours;
+import jalview.schemabinding.version2.Viewport;
+import jalview.schemes.AnnotationColourGradient;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.GraduatedColor;
+import jalview.schemes.ResidueColourScheme;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
-import jalview.ws.jws2.AAConClient;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.dm.AAConSettings;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -46,21 +83,76 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.exolab.castor.xml.Unmarshaller;
+
 /**
  * Write out the current jalview desktop state as a Jalview XML stream.
- *
+ * 
  * Note: the vamsas objects referred to here are primitive versions of the
  * VAMSAS project schema elements - they are not the same and most likely never
  * will be :)
- *
+ * 
  * @author $author$
  * @version $Revision: 1.134 $
  */
 public class Jalview2XML
 {
+  /*
+   * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
+   * of sequence objects are created.
+   */
+  IdentityHashMap<SequenceI, String> seqsToIds = null;
+
+  /**
+   * jalview XML Sequence ID to jalview sequence object reference (both dataset
+   * and alignment sequences. Populated as XML reps of sequence objects are
+   * created.)
+   */
+  Map<String, SequenceI> seqRefIds = null;
+
+  Vector frefedSequence = null;
+
+  boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
+
   /**
    * create/return unique hash string for sq
-   *
+   * 
    * @param sq
    * @return new or existing unique string for sq
    */
@@ -72,7 +164,7 @@ public class Jalview2XML
     }
     if (seqsToIds.containsKey(sq))
     {
-      return (String) seqsToIds.get(sq);
+      return seqsToIds.get(sq);
     }
     else
     {
@@ -113,31 +205,14 @@ public class Jalview2XML
   {
     if (seqsToIds == null)
     {
-      seqsToIds = new IdentityHashMap();
+      seqsToIds = new IdentityHashMap<SequenceI, String>();
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new Hashtable();
+      seqRefIds = new HashMap<String, SequenceI>();
     }
   }
 
-  /**
-   * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
-   * of sequence objects are created.
-   */
-  java.util.IdentityHashMap seqsToIds = null;
-
-  /**
-   * jalview XML Sequence ID to jalview sequence object reference (both dataset
-   * and alignment sequences. Populated as XML reps of sequence objects are
-   * created.)
-   */
-  java.util.Hashtable seqRefIds = null; // key->SequenceI resolution
-
-  Vector frefedSequence = null;
-
-  boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
-
   public Jalview2XML()
   {
   }
@@ -162,7 +237,7 @@ public class Jalview2XML
           {
             if (ref[1] instanceof jalview.datamodel.Mapping)
             {
-              SequenceI seq = (SequenceI) seqRefIds.get(sref);
+              SequenceI seq = seqRefIds.get(sref);
               while (seq.getDatasetSequence() != null)
               {
                 seq = seq.getDatasetSequence();
@@ -173,7 +248,7 @@ public class Jalview2XML
             {
               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
               {
-                SequenceI seq = (SequenceI) seqRefIds.get(sref);
+                SequenceI seq = seqRefIds.get(sref);
                 while (seq.getDatasetSequence() != null)
                 {
                   seq = seq.getDatasetSequence();
@@ -235,16 +310,17 @@ public class Jalview2XML
   /**
    * List of pdbfiles added to Jar
    */
-  Vector pdbfiles = null;
+  List<String> pdbfiles = null;
 
   // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
-  public void SaveState(File statefile)
+  public void saveState(File statefile)
   {
+    FileOutputStream fos = null;
     try
     {
-      FileOutputStream fos = new FileOutputStream(statefile);
+      fos = new FileOutputStream(statefile);
       JarOutputStream jout = new JarOutputStream(fos);
-      SaveState(jout);
+      saveState(jout);
 
     } catch (Exception e)
     {
@@ -260,16 +336,28 @@ public class Jalview2XML
         errorMessage += "(output file was '" + statefile + "')";
       }
       e.printStackTrace();
+    } finally
+    {
+      if (fos != null)
+      {
+        try
+        {
+          fos.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
     }
     reportErrors();
   }
 
   /**
    * Writes a jalview project archive to the given Jar output stream.
-   *
+   * 
    * @param jout
    */
-  public void SaveState(JarOutputStream jout)
+  public void saveState(JarOutputStream jout)
   {
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
@@ -278,13 +366,13 @@ public class Jalview2XML
       return;
     }
 
+    Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
+
     try
     {
 
       // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
       // //////////////////////////////////////////////////
-      // NOTE ALSO new PrintWriter must be used for each new JarEntry
-      PrintWriter out = null;
 
       Vector shortNames = new Vector();
 
@@ -332,6 +420,7 @@ public class Jalview2XML
           }
 
           int ap, apSize = af.alignPanels.size();
+
           for (ap = 0; ap < apSize; ap++)
           {
             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
@@ -342,10 +431,22 @@ public class Jalview2XML
               fileName = fileName + ".xml";
             }
 
-            SaveState(apanel, fileName, jout);
+            saveState(apanel, fileName, jout);
+
+            String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                    .getDataset());
+            if (!dsses.containsKey(dssid))
+            {
+              dsses.put(dssid, af);
+            }
+
           }
         }
       }
+
+      writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
+              jout);
+
       try
       {
         jout.flush();
@@ -367,7 +468,7 @@ public class Jalview2XML
   }
 
   // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
-  public boolean SaveAlignment(AlignFrame af, String jarFile,
+  public boolean saveAlignment(AlignFrame af, String jarFile,
           String fileName)
   {
     try
@@ -375,6 +476,7 @@ public class Jalview2XML
       int ap, apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
+      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
       for (ap = 0; ap < apSize; ap++)
       {
         AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
@@ -384,9 +486,15 @@ public class Jalview2XML
         {
           jfileName = jfileName + ".xml";
         }
-        SaveState(apanel, jfileName, jout);
+        saveState(apanel, jfileName, jout);
+        String dssid = getDatasetIdRef(af.getViewport().getAlignment()
+                .getDataset());
+        if (!dsses.containsKey(dssid))
+        {
+          dsses.put(dssid, af);
+        }
       }
-
+      writeDatasetFor(dsses, fileName, jout);
       try
       {
         jout.flush();
@@ -404,10 +512,26 @@ public class Jalview2XML
     }
   }
 
+  private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
+          String fileName, JarOutputStream jout)
+  {
+
+    for (String dssids : dsses.keySet())
+    {
+      AlignFrame _af = dsses.get(dssids);
+      String jfileName = fileName + " Dataset for " + _af.getTitle();
+      if (!jfileName.endsWith(".xml"))
+      {
+        jfileName = jfileName + ".xml";
+      }
+      saveState(_af.alignPanel, jfileName, true, jout);
+    }
+  }
+
   /**
-   * create a JalviewModel from an algnment view and marshall it to a
+   * create a JalviewModel from an alignment view and marshall it to a
    * JarOutputStream
-   *
+   * 
    * @param ap
    *          panel to create jalview model for
    * @param fileName
@@ -417,12 +541,34 @@ public class Jalview2XML
    * @param out
    *          jar entry name
    */
-  public JalviewModel SaveState(AlignmentPanel ap, String fileName,
+  public JalviewModel saveState(AlignmentPanel ap, String fileName,
           JarOutputStream jout)
   {
+    return saveState(ap, fileName, false, jout);
+  }
+
+  /**
+   * create a JalviewModel from an alignment view and marshall it to a
+   * JarOutputStream
+   * 
+   * @param ap
+   *          panel to create jalview model for
+   * @param fileName
+   *          name of alignment panel written to output stream
+   * @param storeDS
+   *          when true, only write the dataset for the alignment, not the data
+   *          associated with the view.
+   * @param jout
+   *          jar output stream
+   * @param out
+   *          jar entry name
+   */
+  public JalviewModel saveState(AlignmentPanel ap, String fileName,
+          boolean storeDS, JarOutputStream jout)
+  {
     initSeqRefs();
-    Vector jmolViewIds = new Vector(); //
-    Vector userColours = new Vector();
+    List<String> viewIds = new ArrayList<String>();
+    List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
 
     AlignViewport av = ap.av;
 
@@ -430,7 +576,8 @@ public class Jalview2XML
     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
 
     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
-    object.setVersion(jalview.bin.Cache.getProperty("VERSION"));
+    object.setVersion(jalview.bin.Cache.getDefault("VERSION",
+            "Development Build"));
 
     jalview.datamodel.AlignmentI jal = av.getAlignment();
 
@@ -449,6 +596,11 @@ public class Jalview2XML
     {
       // dataset id is the dataset's hashcode
       vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
+      if (storeDS)
+      {
+        // switch jal and the dataset
+        jal = jal.getDataset();
+      }
     }
     if (jal.getProperties() != null)
     {
@@ -464,14 +616,16 @@ public class Jalview2XML
     }
 
     JSeq jseq;
-    Set<String> calcIdSet=new HashSet<String>();
+    Set<String> calcIdSet = new HashSet<String>();
 
     // SAVE SEQUENCES
     String id = "";
-    jalview.datamodel.SequenceI jds;
+    jalview.datamodel.SequenceI jds, jdatasq;
     for (int i = 0; i < jal.getHeight(); i++)
     {
       jds = jal.getSequenceAt(i);
+      jdatasq = jds.getDatasetSequence() == null ? jds : jds
+              .getDatasetSequence();
       id = seqHash(jds);
 
       if (seqRefIds.get(id) != null)
@@ -503,29 +657,34 @@ public class Jalview2XML
       jseq.setColour(av.getSequenceColour(jds).getRGB());
 
       jseq.setId(id); // jseq id should be a string not a number
-
-      if (av.hasHiddenRows())
+      if (!storeDS)
       {
-        jseq.setHidden(av.getAlignment().getHiddenSequences().isHidden(jds));
-
-        if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
+        // Store any sequences this sequence represents
+        if (av.hasHiddenRows())
         {
-          jalview.datamodel.SequenceI[] reps = av.getRepresentedSequences(
-                  jal.getSequenceAt(i)).getSequencesInOrder(jal);
+          jseq.setHidden(av.getAlignment().getHiddenSequences()
+                  .isHidden(jds));
 
-          for (int h = 0; h < reps.length; h++)
+          if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
           {
-            if (reps[h] != jal.getSequenceAt(i))
+            jalview.datamodel.SequenceI[] reps = av
+                    .getRepresentedSequences(jal.getSequenceAt(i))
+                    .getSequencesInOrder(jal);
+
+            for (int h = 0; h < reps.length; h++)
             {
-              jseq.addHiddenSequences(jal.findIndex(reps[h]));
+              if (reps[h] != jal.getSequenceAt(i))
+              {
+                jseq.addHiddenSequences(jal.findIndex(reps[h]));
+              }
             }
           }
         }
       }
 
-      if (jds.getDatasetSequence().getSequenceFeatures() != null)
+      if (jdatasq.getSequenceFeatures() != null)
       {
-        jalview.datamodel.SequenceFeature[] sf = jds.getDatasetSequence()
+        jalview.datamodel.SequenceFeature[] sf = jdatasq
                 .getSequenceFeatures();
         int index = 0;
         while (index < sf.length)
@@ -567,9 +726,9 @@ public class Jalview2XML
         }
       }
 
-      if (jds.getDatasetSequence().getPDBId() != null)
+      if (jdatasq.getPDBId() != null)
       {
-        Enumeration en = jds.getDatasetSequence().getPDBId().elements();
+        Enumeration en = jdatasq.getPDBId().elements();
         while (en.hasMoreElements())
         {
           Pdbids pdb = new Pdbids();
@@ -579,74 +738,21 @@ public class Jalview2XML
           pdb.setId(entry.getId());
           pdb.setType(entry.getType());
 
-          AppJmol jmol;
+          /*
+           * Store any structure views associated with this sequence. This
+           * section copes with duplicate entries in the project, so a dataset
+           * only view *should* be coped with sensibly.
+           */
           // This must have been loaded, is it still visible?
           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
           String matchedFile = null;
           for (int f = frames.length - 1; f > -1; f--)
           {
-            if (frames[f] instanceof AppJmol)
+            if (frames[f] instanceof StructureViewerBase)
             {
-              jmol = (AppJmol) frames[f];
-              for (int peid = 0; peid < jmol.jmb.pdbentry.length; peid++)
-              {
-                if (!jmol.jmb.pdbentry[peid].getId().equals(entry.getId())
-                        && !(entry.getId().length() > 4 && entry
-                                .getId()
-                                .toLowerCase()
-                                .startsWith(
-                                        jmol.jmb.pdbentry[peid].getId()
-                                                .toLowerCase())))
-                  continue;
-                if (matchedFile == null)
-                {
-                  matchedFile = jmol.jmb.pdbentry[peid].getFile();
-                }
-                else if (!matchedFile.equals(jmol.jmb.pdbentry[peid]
-                        .getFile()))
-                {
-                  Cache.log
-                          .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
-                                  + jmol.jmb.pdbentry[peid].getFile());
-                  ; // record the
-                }
-                // file so we
-                // can get at it if the ID
-                // match is ambiguous (e.g.
-                // 1QIP==1qipA)
-                String statestring = jmol.jmb.viewer.getStateInfo();
-
-                for (int smap = 0; smap < jmol.jmb.sequence[peid].length; smap++)
-                {
-                  // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
-                  if (jds == jmol.jmb.sequence[peid][smap])
-                  {
-                    StructureState state = new StructureState();
-                    state.setVisible(true);
-                    state.setXpos(jmol.getX());
-                    state.setYpos(jmol.getY());
-                    state.setWidth(jmol.getWidth());
-                    state.setHeight(jmol.getHeight());
-                    state.setViewId(jmol.getViewId());
-                    state.setAlignwithAlignPanel(jmol.isUsedforaligment(ap));
-                    state.setColourwithAlignPanel(jmol
-                            .isUsedforcolourby(ap));
-                    state.setColourByJmol(jmol.isColouredByJmol());
-                    if (!jmolViewIds.contains(state.getViewId()))
-                    {
-                      // Make sure we only store a Jmol state once in each XML
-                      // document.
-                      jmolViewIds.addElement(state.getViewId());
-                      state.setContent(statestring.replaceAll("\n", ""));
-                    }
-                    else
-                    {
-                      state.setContent("# duplicate state");
-                    }
-                    pdb.addStructureState(state);
-                  }
-                }
-              }
+              StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
+              matchedFile = saveStructureState(ap, jds, pdb, entry,
+                      viewIds, matchedFile, viewFrame);
             }
           }
 
@@ -660,12 +766,13 @@ public class Jalview2XML
             pdb.setFile(matchedFile); // entry.getFile());
             if (pdbfiles == null)
             {
-              pdbfiles = new Vector();
+              pdbfiles = new ArrayList<String>();
             }
 
             if (!pdbfiles.contains(entry.getId()))
             {
-              pdbfiles.addElement(entry.getId());
+              pdbfiles.add(entry.getId());
+              DataInputStream dis = null;
               try
               {
                 File file = new File(matchedFile);
@@ -673,7 +780,7 @@ public class Jalview2XML
                 {
                   byte[] data = new byte[(int) file.length()];
                   jout.putNextEntry(new JarEntry(entry.getId()));
-                  DataInputStream dis = new DataInputStream(
+                  dis = new DataInputStream(
                           new FileInputStream(file));
                   dis.readFully(data);
 
@@ -685,6 +792,18 @@ public class Jalview2XML
               } catch (Exception ex)
               {
                 ex.printStackTrace();
+              } finally
+              {
+                if (dis != null)
+                {
+                  try
+                  {
+                    dis.close();
+                  } catch (IOException e)
+                  {
+                    // ignore
+                  }
+                }
               }
 
             }
@@ -713,7 +832,7 @@ public class Jalview2XML
       jms.addJSeq(jseq);
     }
 
-    if (av.hasHiddenRows())
+    if (!storeDS && av.hasHiddenRows())
     {
       jal = av.getAlignment();
     }
@@ -757,7 +876,7 @@ public class Jalview2XML
 
     // SAVE TREES
     // /////////////////////////////////
-    if (av.currentTree != null)
+    if (!storeDS && av.currentTree != null)
     {
       // FIND ANY ASSOCIATED TREES
       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
@@ -804,139 +923,28 @@ public class Jalview2XML
      * store forward refs from an annotationRow to any groups
      */
     IdentityHashMap groupRefs = new IdentityHashMap();
-    if (jal.getAlignmentAnnotation() != null)
+    if (storeDS)
     {
-      jalview.datamodel.AlignmentAnnotation[] aa = jal
-              .getAlignmentAnnotation();
-
-      for (int i = 0; i < aa.length; i++)
+      for (SequenceI sq : jal.getSequences())
       {
-        Annotation an = new Annotation();
-
-        if (aa[i].annotationId != null)
-        {
-          annotationIds.put(aa[i].annotationId, aa[i]);
-        }
-
-        an.setId(aa[i].annotationId);
-
-        an.setVisible(aa[i].visible);
-
-        an.setDescription(aa[i].description);
-
-        if (aa[i].sequenceRef != null)
+        // Store annotation on dataset sequences only
+        jalview.datamodel.AlignmentAnnotation[] aa = sq.getAnnotation();
+        if (aa != null && aa.length > 0)
         {
-          // TODO later annotation sequenceRef should be the XML ID of the
-          // sequence rather than its display name
-          an.setSequenceRef(aa[i].sequenceRef.getName());
+          storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                  vamsasSet);
         }
-        if (aa[i].groupRef != null)
-        {
-          Object groupIdr = groupRefs.get(aa[i].groupRef);
-          if (groupIdr == null)
-          {
-            // make a locally unique String
-            groupRefs.put(aa[i].groupRef,
-                    groupIdr = ("" + System.currentTimeMillis()
-                            + aa[i].groupRef.getName() + groupRefs.size()));
-          }
-          an.setGroupRef(groupIdr.toString());
-        }
-
-        // store all visualization attributes for annotation
-        an.setGraphHeight(aa[i].graphHeight);
-        an.setCentreColLabels(aa[i].centreColLabels);
-        an.setScaleColLabels(aa[i].scaleColLabel);
-        an.setShowAllColLabels(aa[i].showAllColLabels);
-        an.setBelowAlignment(aa[i].belowAlignment);
-
-        if (aa[i].graph > 0)
-        {
-          an.setGraph(true);
-          an.setGraphType(aa[i].graph);
-          an.setGraphGroup(aa[i].graphGroup);
-          if (aa[i].getThreshold() != null)
-          {
-            ThresholdLine line = new ThresholdLine();
-            line.setLabel(aa[i].getThreshold().label);
-            line.setValue(aa[i].getThreshold().value);
-            line.setColour(aa[i].getThreshold().colour.getRGB());
-            an.setThresholdLine(line);
-          }
-        }
-        else
-        {
-          an.setGraph(false);
-        }
-
-        an.setLabel(aa[i].label);
-
-        if (aa[i] == av.getAlignmentQualityAnnot()
-                || aa[i] == av.getAlignmentConservationAnnotation()
-                || aa[i] == av.getAlignmentConsensusAnnotation()
-                || aa[i].autoCalculated)
-        {
-          // new way of indicating autocalculated annotation -
-          an.setAutoCalculated(aa[i].autoCalculated);
-        }
-        if (aa[i].hasScore())
-        {
-          an.setScore(aa[i].getScore());
-        }
-
-        if (aa[i].getCalcId() != null)
-        {
-          calcIdSet.add(aa[i].getCalcId());
-          an.setCalcId(aa[i].getCalcId());
-        }
-
-        AnnotationElement ae;
-        if (aa[i].annotations != null)
-        {
-          an.setScoreOnly(false);
-          for (int a = 0; a < aa[i].annotations.length; a++)
-          {
-            if ((aa[i] == null) || (aa[i].annotations[a] == null))
-            {
-              continue;
-            }
-
-            ae = new AnnotationElement();
-            if (aa[i].annotations[a].description != null)
-              ae.setDescription(aa[i].annotations[a].description);
-            if (aa[i].annotations[a].displayCharacter != null)
-              ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
-
-            if (!Float.isNaN(aa[i].annotations[a].value))
-              ae.setValue(aa[i].annotations[a].value);
-
-            ae.setPosition(a);
-            if (aa[i].annotations[a].secondaryStructure != ' '
-                    && aa[i].annotations[a].secondaryStructure != '\0')
-              ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
-                      + "");
-
-            if (aa[i].annotations[a].colour != null
-                    && aa[i].annotations[a].colour != java.awt.Color.black)
-            {
-              ae.setColour(aa[i].annotations[a].colour.getRGB());
-            }
-
-            an.addAnnotationElement(ae);
-            if (aa[i].autoCalculated)
-            {
-              // only write one non-null entry into the annotation row -
-              // sufficient to get the visualization attributes necessary to
-              // display data
-              continue;
-            }
-          }
-        }
-        else
-        {
-          an.setScoreOnly(true);
-        }
-        vamsasSet.addAnnotation(an);
+      }
+    }
+    else
+    {
+      if (jal.getAlignmentAnnotation() != null)
+      {
+        // Store the annotation shown on the alignment.
+        jalview.datamodel.AlignmentAnnotation[] aa = jal
+                .getAlignmentAnnotation();
+        storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                vamsasSet);
       }
     }
     // SAVE GROUPS
@@ -964,7 +972,7 @@ public class Jalview2XML
 
             if (sg.cs instanceof jalview.schemes.UserColourScheme)
             {
-              groups[i].setColour(SetUserColourScheme(sg.cs, userColours,
+              groups[i].setColour(setUserColourScheme(sg.cs, userColours,
                       jms));
             }
             else
@@ -975,15 +983,15 @@ public class Jalview2XML
           }
           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
           {
-            groups[i]
-                    .setColour(ColourSchemeProperty
-                            .getColourName(((jalview.schemes.AnnotationColourGradient) sg.cs)
-                                    .getBaseColour()));
+            groups[i].setColour("AnnotationColourGradient");
+            groups[i].setAnnotationColours(constructAnnotationColours(
+                    (jalview.schemes.AnnotationColourGradient) sg.cs,
+                    userColours, jms));
           }
           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
           {
             groups[i]
-                    .setColour(SetUserColourScheme(sg.cs, userColours, jms));
+                    .setColour(setUserColourScheme(sg.cs, userColours, jms));
           }
           else
           {
@@ -1004,6 +1012,7 @@ public class Jalview2XML
         groups[i].setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
         groups[i].setShowConsensusHistogram(sg.isShowConsensusHistogram());
         groups[i].setShowSequenceLogo(sg.isShowSequenceLogo());
+        groups[i].setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
         for (int s = 0; s < sg.getSize(); s++)
         {
           jalview.datamodel.Sequence seq = (jalview.datamodel.Sequence) sg
@@ -1014,276 +1023,537 @@ public class Jalview2XML
 
       jms.setJGroup(groups);
     }
+    if (!storeDS)
+    {
+      // /////////SAVE VIEWPORT
+      Viewport view = new Viewport();
+      view.setTitle(ap.alignFrame.getTitle());
+      view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
+              av.getSequenceSetId()));
+      view.setId(av.getViewId());
+      view.setViewName(av.viewName);
+      view.setGatheredViews(av.gatherViewsHere);
+
+      if (ap.av.explodedPosition != null)
+      {
+        view.setXpos(av.explodedPosition.x);
+        view.setYpos(av.explodedPosition.y);
+        view.setWidth(av.explodedPosition.width);
+        view.setHeight(av.explodedPosition.height);
+      }
+      else
+      {
+        view.setXpos(ap.alignFrame.getBounds().x);
+        view.setYpos(ap.alignFrame.getBounds().y);
+        view.setWidth(ap.alignFrame.getBounds().width);
+        view.setHeight(ap.alignFrame.getBounds().height);
+      }
 
-    // /////////SAVE VIEWPORT
-    Viewport view = new Viewport();
-    view.setTitle(ap.alignFrame.getTitle());
-    view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
-            av.getSequenceSetId()));
-    view.setId(av.getViewId());
-    view.setViewName(av.viewName);
-    view.setGatheredViews(av.gatherViewsHere);
-
-    if (ap.av.explodedPosition != null)
-    {
-      view.setXpos(av.explodedPosition.x);
-      view.setYpos(av.explodedPosition.y);
-      view.setWidth(av.explodedPosition.width);
-      view.setHeight(av.explodedPosition.height);
-    }
-    else
-    {
-      view.setXpos(ap.alignFrame.getBounds().x);
-      view.setYpos(ap.alignFrame.getBounds().y);
-      view.setWidth(ap.alignFrame.getBounds().width);
-      view.setHeight(ap.alignFrame.getBounds().height);
-    }
-
-    view.setStartRes(av.startRes);
-    view.setStartSeq(av.startSeq);
-
-    if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
-    {
-      view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
-              userColours, jms));
-    }
-    else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
-    {
-      jalview.schemes.AnnotationColourGradient acg = (jalview.schemes.AnnotationColourGradient) av
-              .getGlobalColourScheme();
+      view.setStartRes(av.startRes);
+      view.setStartSeq(av.startSeq);
 
-      AnnotationColours ac = new AnnotationColours();
-      ac.setAboveThreshold(acg.getAboveThreshold());
-      ac.setThreshold(acg.getAnnotationThreshold());
-      ac.setAnnotation(acg.getAnnotation());
-      if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+      if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
-        ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
+        view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
                 userColours, jms));
       }
+      else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+      {
+        AnnotationColours ac = constructAnnotationColours(
+                (jalview.schemes.AnnotationColourGradient) av
+                        .getGlobalColourScheme(),
+                userColours, jms);
+
+        view.setAnnotationColours(ac);
+        view.setBgColour("AnnotationColourGradient");
+      }
       else
       {
-        ac.setColourScheme(ColourSchemeProperty.getColourName(acg
-                .getBaseColour()));
+        view.setBgColour(ColourSchemeProperty.getColourName(av
+                .getGlobalColourScheme()));
       }
 
-      ac.setMaxColour(acg.getMaxColour().getRGB());
-      ac.setMinColour(acg.getMinColour().getRGB());
-      view.setAnnotationColours(ac);
-      view.setBgColour("AnnotationColourGradient");
-    }
-    else
-    {
-      view.setBgColour(ColourSchemeProperty.getColourName(av
-              .getGlobalColourScheme()));
-    }
+      ColourSchemeI cs = av.getGlobalColourScheme();
 
-    ColourSchemeI cs = av.getGlobalColourScheme();
-
-    if (cs != null)
-    {
-      if (cs.conservationApplied())
+      if (cs != null)
       {
-        view.setConsThreshold(cs.getConservationInc());
-        if (cs instanceof jalview.schemes.UserColourScheme)
+        if (cs.conservationApplied())
+        {
+          view.setConsThreshold(cs.getConservationInc());
+          if (cs instanceof jalview.schemes.UserColourScheme)
+          {
+            view.setBgColour(setUserColourScheme(cs, userColours, jms));
+          }
+        }
+
+        if (cs instanceof ResidueColourScheme)
         {
-          view.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          view.setPidThreshold(cs.getThreshold());
         }
       }
 
-      if (cs instanceof ResidueColourScheme)
-      {
-        view.setPidThreshold(cs.getThreshold());
-      }
-    }
-
-    view.setConservationSelected(av.getConservationSelected());
-    view.setPidSelected(av.getAbovePIDThreshold());
-    view.setFontName(av.font.getName());
-    view.setFontSize(av.font.getSize());
-    view.setFontStyle(av.font.getStyle());
-    view.setRenderGaps(av.renderGaps);
-    view.setShowAnnotation(av.getShowAnnotation());
-    view.setShowBoxes(av.getShowBoxes());
-    view.setShowColourText(av.getColourText());
-    view.setShowFullId(av.getShowJVSuffix());
-    view.setRightAlignIds(av.rightAlignIds);
-    view.setShowSequenceFeatures(av.showSequenceFeatures);
-    view.setShowText(av.getShowText());
-    view.setShowUnconserved(av.getShowUnconserved());
-    view.setWrapAlignment(av.getWrapAlignment());
-    view.setTextCol1(av.textColour.getRGB());
-    view.setTextCol2(av.textColour2.getRGB());
-    view.setTextColThreshold(av.thresholdTextColour);
-    view.setShowConsensusHistogram(av.isShowConsensusHistogram());
-    view.setShowSequenceLogo(av.isShowSequenceLogo());
-    view.setShowGroupConsensus(av.isShowGroupConsensus());
-    view.setShowGroupConservation(av.isShowGroupConservation());
-    view.setShowNPfeatureTooltip(av.isShowNpFeats());
-    view.setShowDbRefTooltip(av.isShowDbRefs());
-    view.setFollowHighlight(av.followHighlight);
-    view.setFollowSelection(av.followSelection);
-    view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
-    if (av.featuresDisplayed != null)
-    {
-      jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
-
-      String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
-
-      Vector settingsAdded = new Vector();
-      Object gstyle = null;
-      GraduatedColor gcol = null;
-      if (renderOrder != null)
-      {
-        for (int ro = 0; ro < renderOrder.length; ro++)
+      view.setConservationSelected(av.getConservationSelected());
+      view.setPidSelected(av.getAbovePIDThreshold());
+      view.setFontName(av.font.getName());
+      view.setFontSize(av.font.getSize());
+      view.setFontStyle(av.font.getStyle());
+      view.setRenderGaps(av.renderGaps);
+      view.setShowAnnotation(av.isShowAnnotation());
+      view.setShowBoxes(av.getShowBoxes());
+      view.setShowColourText(av.getColourText());
+      view.setShowFullId(av.getShowJVSuffix());
+      view.setRightAlignIds(av.isRightAlignIds());
+      view.setShowSequenceFeatures(av.isShowSequenceFeatures());
+      view.setShowText(av.getShowText());
+      view.setShowUnconserved(av.getShowUnconserved());
+      view.setWrapAlignment(av.getWrapAlignment());
+      view.setTextCol1(av.textColour.getRGB());
+      view.setTextCol2(av.textColour2.getRGB());
+      view.setTextColThreshold(av.thresholdTextColour);
+      view.setShowConsensusHistogram(av.isShowConsensusHistogram());
+      view.setShowSequenceLogo(av.isShowSequenceLogo());
+      view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
+      view.setShowGroupConsensus(av.isShowGroupConsensus());
+      view.setShowGroupConservation(av.isShowGroupConservation());
+      view.setShowNPfeatureTooltip(av.isShowNpFeats());
+      view.setShowDbRefTooltip(av.isShowDbRefs());
+      view.setFollowHighlight(av.followHighlight);
+      view.setFollowSelection(av.followSelection);
+      view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
+      if (av.getFeaturesDisplayed() != null)
+      {
+        jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
+
+        String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getRenderOrder().toArray(new String[0]);
+
+        Vector settingsAdded = new Vector();
+        Object gstyle = null;
+        GraduatedColor gcol = null;
+        if (renderOrder != null)
         {
-          gstyle = ap.seqPanel.seqCanvas.getFeatureRenderer()
-                  .getFeatureStyle(renderOrder[ro]);
-          Setting setting = new Setting();
-          setting.setType(renderOrder[ro]);
-          if (gstyle instanceof GraduatedColor)
+          for (int ro = 0; ro < renderOrder.length; ro++)
           {
-            gcol = (GraduatedColor) gstyle;
-            setting.setColour(gcol.getMaxColor().getRGB());
-            setting.setMincolour(gcol.getMinColor().getRGB());
-            setting.setMin(gcol.getMin());
-            setting.setMax(gcol.getMax());
-            setting.setColourByLabel(gcol.isColourByLabel());
-            setting.setAutoScale(gcol.isAutoScale());
-            setting.setThreshold(gcol.getThresh());
-            setting.setThreshstate(gcol.getThreshType());
+            gstyle = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                    .getFeatureStyle(renderOrder[ro]);
+            Setting setting = new Setting();
+            setting.setType(renderOrder[ro]);
+            if (gstyle instanceof GraduatedColor)
+            {
+              gcol = (GraduatedColor) gstyle;
+              setting.setColour(gcol.getMaxColor().getRGB());
+              setting.setMincolour(gcol.getMinColor().getRGB());
+              setting.setMin(gcol.getMin());
+              setting.setMax(gcol.getMax());
+              setting.setColourByLabel(gcol.isColourByLabel());
+              setting.setAutoScale(gcol.isAutoScale());
+              setting.setThreshold(gcol.getThresh());
+              setting.setThreshstate(gcol.getThreshType());
+            }
+            else
+            {
+              setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                      .getColour(renderOrder[ro]).getRGB());
+            }
+
+            setting.setDisplay(av.getFeaturesDisplayed().isVisible(
+                    renderOrder[ro]));
+            float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                    .getOrder(renderOrder[ro]);
+            if (rorder > -1)
+            {
+              setting.setOrder(rorder);
+            }
+            fs.addSetting(setting);
+            settingsAdded.addElement(renderOrder[ro]);
           }
-          else
+        }
+
+        // Make sure we save none displayed feature settings
+        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                .getFeatureColours().keySet().iterator();
+        while (en.hasNext())
+        {
+          String key = en.next().toString();
+          if (settingsAdded.contains(key))
           {
-            setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
-                    .getColour(renderOrder[ro]).getRGB());
+            continue;
           }
 
-          setting.setDisplay(av.featuresDisplayed
-                  .containsKey(renderOrder[ro]));
-          float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
-                  .getOrder(renderOrder[ro]);
+          Setting setting = new Setting();
+          setting.setType(key);
+          setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                  .getColour(key).getRGB());
+
+          setting.setDisplay(false);
+          float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+                  .getOrder(key);
           if (rorder > -1)
           {
             setting.setOrder(rorder);
           }
           fs.addSetting(setting);
-          settingsAdded.addElement(renderOrder[ro]);
+          settingsAdded.addElement(key);
         }
+        // is groups actually supposed to be a map here ?
+        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().getFeatureGroups()
+                .iterator();
+        Vector groupsAdded = new Vector();
+        while (en.hasNext())
+        {
+          String grp = en.next().toString();
+          if (groupsAdded.contains(grp))
+          {
+            continue;
+          }
+          Group g = new Group();
+          g.setName(grp);
+          g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
+                  .getFeatureRenderer().checkGroupVisibility(grp, false))
+                  .booleanValue());
+          fs.addGroup(g);
+          groupsAdded.addElement(grp);
+        }
+        jms.setFeatureSettings(fs);
+
       }
 
-      // Make sure we save none displayed feature settings
-      Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
-              .keySet().iterator();
-      while (en.hasNext())
+      if (av.hasHiddenColumns())
       {
-        String key = en.next().toString();
-        if (settingsAdded.contains(key))
+        if (av.getColumnSelection() == null
+                || av.getColumnSelection().getHiddenColumns() == null)
         {
-          continue;
+          warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
         }
-
-        Setting setting = new Setting();
-        setting.setType(key);
-        setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
-                .getColour(key).getRGB());
-
-        setting.setDisplay(false);
-        float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
-                key);
-        if (rorder > -1)
+        else
         {
-          setting.setOrder(rorder);
+          for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
+                  .size(); c++)
+          {
+            int[] region = av.getColumnSelection()
+                    .getHiddenColumns().get(c);
+            HiddenColumns hc = new HiddenColumns();
+            hc.setStart(region[0]);
+            hc.setEnd(region[1]);
+            view.addHiddenColumns(hc);
+          }
         }
-        fs.addSetting(setting);
-        settingsAdded.addElement(key);
       }
-      en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
-              .keySet().iterator();
-      Vector groupsAdded = new Vector();
-      while (en.hasNext())
+      if (calcIdSet.size() > 0)
       {
-        String grp = en.next().toString();
-        if (groupsAdded.contains(grp))
+        for (String calcId : calcIdSet)
         {
-          continue;
+          if (calcId.trim().length() > 0)
+          {
+            CalcIdParam cidp = createCalcIdParam(calcId, av);
+            // Some calcIds have no parameters.
+            if (cidp != null)
+            {
+              view.addCalcIdParam(cidp);
+            }
+          }
         }
-        Group g = new Group();
-        g.setName(grp);
-        g.setDisplay(((Boolean) ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
-                .get(grp)).booleanValue());
-        fs.addGroup(g);
-        groupsAdded.addElement(grp);
       }
-      jms.setFeatureSettings(fs);
 
+      jms.addViewport(view);
     }
+    object.setJalviewModelSequence(jms);
+    object.getVamsasModel().addSequenceSet(vamsasSet);
 
-    if (av.hasHiddenColumns())
+    if (jout != null && fileName != null)
     {
-      if (av.getColumnSelection() == null
-              || av.getColumnSelection().getHiddenColumns() == null)
+      // We may not want to write the object to disk,
+      // eg we can copy the alignViewport to a new view object
+      // using save and then load
+      try
+      {
+        JarEntry entry = new JarEntry(fileName);
+        jout.putNextEntry(entry);
+        PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
+                "UTF-8"));
+        org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
+                pout);
+        marshaller.marshal(object);
+        pout.flush();
+        jout.closeEntry();
+      } catch (Exception ex)
       {
-        warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+        // TODO: raise error in GUI if marshalling failed.
+        ex.printStackTrace();
       }
-      else
+    }
+    return object;
+  }
+
+  /**
+   * Save the state of a structure viewer
+   * 
+   * @param ap
+   * @param jds
+   * @param pdb
+   *          the archive XML element under which to save the state
+   * @param entry
+   * @param viewIds
+   * @param matchedFile
+   * @param viewFrame
+   * @return
+   */
+  protected String saveStructureState(AlignmentPanel ap, SequenceI jds,
+          Pdbids pdb, PDBEntry entry, List<String> viewIds,
+          String matchedFile, StructureViewerBase viewFrame)
+  {
+    final AAStructureBindingModel bindingModel = viewFrame
+            .getBinding();
+    for (int peid = 0; peid < bindingModel
+            .getPdbCount(); peid++)
+    {
+      final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
+      final String pdbId = pdbentry.getId();
+      if (!pdbId.equals(entry.getId())
+              && !(entry.getId().length() > 4 && entry.getId()
+                      .toLowerCase()
+                      .startsWith(pdbId.toLowerCase())))
+      {
+        continue;
+      }
+      if (matchedFile == null)
+      {
+        matchedFile = pdbentry.getFile();
+      }
+      else if (!matchedFile.equals(pdbentry
+              .getFile()))
       {
-        for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
-                .size(); c++)
+        Cache.log
+                .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
+                        + pdbentry.getFile());
+      }
+      // record the
+      // file so we
+      // can get at it if the ID
+      // match is ambiguous (e.g.
+      // 1QIP==1qipA)
+      String statestring = viewFrame.getStateInfo();
+
+      for (int smap = 0; smap < viewFrame.getBinding()
+              .getSequence()[peid].length; smap++)
+      {
+        // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+        if (jds == viewFrame.getBinding().getSequence()[peid][smap])
         {
-          int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
-                  .elementAt(c);
-          HiddenColumns hc = new HiddenColumns();
-          hc.setStart(region[0]);
-          hc.setEnd(region[1]);
-          view.addHiddenColumns(hc);
+          StructureState state = new StructureState();
+          state.setVisible(true);
+          state.setXpos(viewFrame.getX());
+          state.setYpos(viewFrame.getY());
+          state.setWidth(viewFrame.getWidth());
+          state.setHeight(viewFrame.getHeight());
+          final String viewId = viewFrame.getViewId();
+          state.setViewId(viewId);
+          state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
+          state.setColourwithAlignPanel(viewFrame
+                  .isUsedforcolourby(ap));
+          state.setColourByJmol(viewFrame.isColouredByViewer());
+          /*
+           * Only store each structure viewer's state once in each XML document.
+           */
+          if (!viewIds.contains(viewId))
+          {
+            viewIds.add(viewId);
+            state.setContent(statestring.replaceAll("\n", ""));
+          }
+          else
+          {
+            state.setContent("# duplicate state");
+          }
+          pdb.addStructureState(state);
         }
       }
     }
-    if (calcIdSet.size() > 0)
+    return matchedFile;
+  }
+
+  private AnnotationColours constructAnnotationColours(
+          AnnotationColourGradient acg, List<UserColourScheme> userColours,
+          JalviewModelSequence jms)
+  {
+    AnnotationColours ac = new AnnotationColours();
+    ac.setAboveThreshold(acg.getAboveThreshold());
+    ac.setThreshold(acg.getAnnotationThreshold());
+    ac.setAnnotation(acg.getAnnotation());
+    if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+    {
+      ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
+              userColours, jms));
+    }
+    else
+    {
+      ac.setColourScheme(ColourSchemeProperty.getColourName(acg
+              .getBaseColour()));
+    }
+
+    ac.setMaxColour(acg.getMaxColour().getRGB());
+    ac.setMinColour(acg.getMinColour().getRGB());
+    ac.setPerSequence(acg.isSeqAssociated());
+    ac.setPredefinedColours(acg.isPredefinedColours());
+    return ac;
+  }
+
+  private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
+          IdentityHashMap groupRefs, AlignmentViewport av,
+          Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
+  {
+
+    for (int i = 0; i < aa.length; i++)
     {
-      for (String calcId : calcIdSet)
+      Annotation an = new Annotation();
+
+      if (aa[i].annotationId != null)
       {
-        if (calcId.trim().length() > 0)
+        annotationIds.put(aa[i].annotationId, aa[i]);
+      }
+
+      an.setId(aa[i].annotationId);
+
+      an.setVisible(aa[i].visible);
+
+      an.setDescription(aa[i].description);
+
+      if (aa[i].sequenceRef != null)
+      {
+        // TODO later annotation sequenceRef should be the XML ID of the
+        // sequence rather than its display name
+        an.setSequenceRef(aa[i].sequenceRef.getName());
+      }
+      if (aa[i].groupRef != null)
+      {
+        Object groupIdr = groupRefs.get(aa[i].groupRef);
+        if (groupIdr == null)
         {
-          CalcIdParam cidp = createCalcIdParam(calcId, av);
-          // Some calcIds have no parameters.
-          if (cidp != null)
+          // make a locally unique String
+          groupRefs.put(aa[i].groupRef,
+                  groupIdr = ("" + System.currentTimeMillis()
+                          + aa[i].groupRef.getName() + groupRefs.size()));
+        }
+        an.setGroupRef(groupIdr.toString());
+      }
+
+      // store all visualization attributes for annotation
+      an.setGraphHeight(aa[i].graphHeight);
+      an.setCentreColLabels(aa[i].centreColLabels);
+      an.setScaleColLabels(aa[i].scaleColLabel);
+      an.setShowAllColLabels(aa[i].showAllColLabels);
+      an.setBelowAlignment(aa[i].belowAlignment);
+
+      if (aa[i].graph > 0)
+      {
+        an.setGraph(true);
+        an.setGraphType(aa[i].graph);
+        an.setGraphGroup(aa[i].graphGroup);
+        if (aa[i].getThreshold() != null)
+        {
+          ThresholdLine line = new ThresholdLine();
+          line.setLabel(aa[i].getThreshold().label);
+          line.setValue(aa[i].getThreshold().value);
+          line.setColour(aa[i].getThreshold().colour.getRGB());
+          an.setThresholdLine(line);
+        }
+      }
+      else
+      {
+        an.setGraph(false);
+      }
+
+      an.setLabel(aa[i].label);
+
+      if (aa[i] == av.getAlignmentQualityAnnot()
+              || aa[i] == av.getAlignmentConservationAnnotation()
+              || aa[i] == av.getAlignmentConsensusAnnotation()
+              || aa[i].autoCalculated)
+      {
+        // new way of indicating autocalculated annotation -
+        an.setAutoCalculated(aa[i].autoCalculated);
+      }
+      if (aa[i].hasScore())
+      {
+        an.setScore(aa[i].getScore());
+      }
+
+      if (aa[i].getCalcId() != null)
+      {
+        calcIdSet.add(aa[i].getCalcId());
+        an.setCalcId(aa[i].getCalcId());
+      }
+      if (aa[i].hasProperties())
+      {
+        for (String pr : aa[i].getProperties())
+        {
+          Property prop = new Property();
+          prop.setName(pr);
+          prop.setValue(aa[i].getProperty(pr));
+          an.addProperty(prop);
+        }
+      }
+
+      AnnotationElement ae;
+      if (aa[i].annotations != null)
+      {
+        an.setScoreOnly(false);
+        for (int a = 0; a < aa[i].annotations.length; a++)
+        {
+          if ((aa[i] == null) || (aa[i].annotations[a] == null))
+          {
+            continue;
+          }
+
+          ae = new AnnotationElement();
+          if (aa[i].annotations[a].description != null)
+          {
+            ae.setDescription(aa[i].annotations[a].description);
+          }
+          if (aa[i].annotations[a].displayCharacter != null)
+          {
+            ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
+          }
+
+          if (!Float.isNaN(aa[i].annotations[a].value))
+          {
+            ae.setValue(aa[i].annotations[a].value);
+          }
+
+          ae.setPosition(a);
+          if (aa[i].annotations[a].secondaryStructure > ' ')
+          {
+            ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
+                    + "");
+          }
+
+          if (aa[i].annotations[a].colour != null
+                  && aa[i].annotations[a].colour != java.awt.Color.black)
+          {
+            ae.setColour(aa[i].annotations[a].colour.getRGB());
+          }
+
+          an.addAnnotationElement(ae);
+          if (aa[i].autoCalculated)
           {
-            view.addCalcIdParam(cidp);
+            // only write one non-null entry into the annotation row -
+            // sufficient to get the visualization attributes necessary to
+            // display data
+            continue;
           }
         }
       }
-    }
-
-    jms.addViewport(view);
-
-    object.setJalviewModelSequence(jms);
-    object.getVamsasModel().addSequenceSet(vamsasSet);
-
-    if (jout != null && fileName != null)
-    {
-      // We may not want to write the object to disk,
-      // eg we can copy the alignViewport to a new view object
-      // using save and then load
-      try
+      else
       {
-        JarEntry entry = new JarEntry(fileName);
-        jout.putNextEntry(entry);
-        PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
-                "UTF-8"));
-        org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
-                pout);
-        marshaller.marshal(object);
-        pout.flush();
-        jout.closeEntry();
-      } catch (Exception ex)
+        an.setScoreOnly(true);
+      }
+      if (!storeDS || (storeDS && !aa[i].autoCalculated))
       {
-        // TODO: raise error in GUI if marshalling failed.
-        ex.printStackTrace();
+        // skip autocalculated annotation - these are only provided for
+        // alignments
+        vamsasSet.addAnnotation(an);
       }
     }
-    return object;
+
   }
 
   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
@@ -1375,8 +1645,7 @@ public class Jalview2XML
         return false;
       }
     }
-    throw new Error("Unsupported Version for calcIdparam "
-            + calcIdParam.toString());
+    throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
   }
 
   /**
@@ -1557,8 +1826,8 @@ public class Jalview2XML
     return mp;
   }
 
-  String SetUserColourScheme(jalview.schemes.ColourSchemeI cs,
-          Vector userColours, JalviewModelSequence jms)
+  String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
+          List<UserColourScheme> userColours, JalviewModelSequence jms)
   {
     String id = null;
     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
@@ -1603,7 +1872,7 @@ public class Jalview2XML
     return id;
   }
 
-  jalview.schemes.UserColourScheme GetUserColourScheme(
+  jalview.schemes.UserColourScheme getUserColourScheme(
           JalviewModelSequence jms, String id)
   {
     UserColours[] uc = jms.getUserColours();
@@ -1661,24 +1930,42 @@ public class Jalview2XML
    * @param file
    *          - HTTP URL or filename
    */
-  public AlignFrame LoadJalviewAlign(final String file)
+  public AlignFrame loadJalviewAlign(final String file)
   {
 
     jalview.gui.AlignFrame af = null;
 
     try
     {
+      // create list to store references for any new Jmol viewers created
+      newStructureViewers = new Vector<JalviewStructureDisplayI>();
       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
       // Workaround is to make sure caller implements the JarInputStreamProvider
       // interface
       // so we can re-open the jar input stream for each entry.
 
       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
-      af = LoadJalviewAlign(jprovider);
+      af = loadJalviewAlign(jprovider);
+
     } catch (MalformedURLException e)
     {
       errorMessage = "Invalid URL format for '" + file + "'";
       reportErrors();
+    } finally
+    {
+      try
+      {
+        SwingUtilities.invokeAndWait(new Runnable()
+        {
+          public void run()
+          {
+            setLoadingFinishedForNewStructureViewers();
+          };
+        });
+      } catch (Exception x)
+      {
+
+      }
     }
     return af;
   }
@@ -1731,7 +2018,7 @@ public class Jalview2XML
    * @param jprovider
    * @return
    */
-  public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
+  public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
   {
     errorMessage = null;
     if (uniqueSetSuffix == null)
@@ -1740,7 +2027,7 @@ public class Jalview2XML
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new Hashtable();
+      seqRefIds = new HashMap<String, SequenceI>();
     }
     if (viewportsAdded == null)
     {
@@ -1751,7 +2038,7 @@ public class Jalview2XML
       frefedSequence = new Vector();
     }
 
-    jalview.gui.AlignFrame af = null;
+    jalview.gui.AlignFrame af = null, _af = null;
     Hashtable gatherToThisFrame = new Hashtable();
     final String file = jprovider.getFilename();
     try
@@ -1778,10 +2065,14 @@ public class Jalview2XML
           object = (JalviewModel) unmar.unmarshal(in);
           if (true) // !skipViewport(object))
           {
-            af = LoadFromObject(object, file, true, jprovider);
-            if (af.viewport.gatherViewsHere)
+            _af = loadFromObject(object, file, true, jprovider);
+            if (object.getJalviewModelSequence().getViewportCount() > 0)
             {
-              gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+              af = _af;
+              if (af.viewport.gatherViewsHere)
+              {
+                gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+              }
             }
           }
           entryCount++;
@@ -1897,7 +2188,7 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Hashtable alreadyLoadedPDB;
+  Hashtable<String, String> alreadyLoadedPDB;
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
@@ -1909,10 +2200,14 @@ public class Jalview2XML
   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
   {
     if (alreadyLoadedPDB == null)
+    {
       alreadyLoadedPDB = new Hashtable();
+    }
 
     if (alreadyLoadedPDB.containsKey(pdbId))
+    {
       return alreadyLoadedPDB.get(pdbId).toString();
+    }
 
     try
     {
@@ -1996,7 +2291,7 @@ public class Jalview2XML
    *          data source provider
    * @return alignment frame created from view stored in DOM
    */
-  AlignFrame LoadFromObject(JalviewModel object, String file,
+  AlignFrame loadFromObject(JalviewModel object, String file,
           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
   {
     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
@@ -2004,7 +2299,9 @@ public class Jalview2XML
 
     JalviewModelSequence jms = object.getJalviewModelSequence();
 
-    Viewport view = jms.getViewport(0);
+    Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
+            : null;
+
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -2015,11 +2312,11 @@ public class Jalview2XML
 
     boolean multipleView = false;
 
-    JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
+    JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
     int vi = 0; // counter in vamsasSeq array
-    for (int i = 0; i < JSEQ.length; i++)
+    for (int i = 0; i < jseqs.length; i++)
     {
-      String seqId = JSEQ[i].getId();
+      String seqId = jseqs[i].getId();
 
       if (seqRefIds.get(seqId) != null)
       {
@@ -2031,15 +2328,15 @@ public class Jalview2XML
         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
                 vamsasSeq[vi].getSequence());
         jseq.setDescription(vamsasSeq[vi].getDescription());
-        jseq.setStart(JSEQ[i].getStart());
-        jseq.setEnd(JSEQ[i].getEnd());
+        jseq.setStart(jseqs[i].getStart());
+        jseq.setEnd(jseqs[i].getEnd());
         jseq.setVamsasId(uniqueSetSuffix + seqId);
         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
         tmpseqs.add(jseq);
         vi++;
       }
 
-      if (JSEQ[i].getHidden())
+      if (jseqs[i].getHidden())
       {
         if (hiddenSeqs == null)
         {
@@ -2080,7 +2377,10 @@ public class Jalview2XML
     }
     else
     {
-      recoverDatasetFor(vamsasSet, al);
+      // recover dataset - passing on flag indicating if this a 'viewless'
+      // sequence set (a.k.a. a stored dataset for the project)
+      recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
+              .getViewportCount() == 0);
     }
     // ///////////////////////////////
 
@@ -2091,9 +2391,9 @@ public class Jalview2XML
       // structures for the alignment
       for (int i = 0; i < vamsasSeq.length; i++)
       {
-        if (JSEQ[i].getFeaturesCount() > 0)
+        if (jseqs[i].getFeaturesCount() > 0)
         {
-          Features[] features = JSEQ[i].getFeatures();
+          Features[] features = jseqs[i].getFeatures();
           for (int f = 0; f < features.length; f++)
           {
             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
@@ -2123,9 +2423,9 @@ public class Jalview2XML
         {
           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
         }
-        if (JSEQ[i].getPdbidsCount() > 0)
+        if (jseqs[i].getPdbidsCount() > 0)
         {
-          Pdbids[] ids = JSEQ[i].getPdbids();
+          Pdbids[] ids = jseqs[i].getPdbids();
           for (int p = 0; p < ids.length; p++)
           {
             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
@@ -2142,7 +2442,9 @@ public class Jalview2XML
                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
               }
             }
-
+            StructureSelectionManager.getStructureSelectionManager(
+                    Desktop.instance)
+                    .registerPDBEntry(entry);
             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
           }
         }
@@ -2186,7 +2488,7 @@ public class Jalview2XML
           AlcodMap[] maps = alc[i].getAlcodMap();
           for (int m = 0; m < maps.length; m++)
           {
-            SequenceI dnaseq = (SequenceI) seqRefIds
+            SequenceI dnaseq = seqRefIds
                     .get(maps[m].getDnasq());
             // Load Mapping
             jalview.datamodel.Mapping mapping = null;
@@ -2258,7 +2560,9 @@ public class Jalview2XML
           // in principle Visible should always be true for annotation displayed
           // in multiple views
           if (an[i].hasVisible())
+          {
             jda.visible = an[i].getVisible();
+          }
 
           al.addAnnotation(jda);
 
@@ -2267,7 +2571,7 @@ public class Jalview2XML
         // Construct new annotation from model.
         AnnotationElement[] ae = an[i].getAnnotationElement();
         jalview.datamodel.Annotation[] anot = null;
-        java.awt.Color firstColour=null;
+        java.awt.Color firstColour = null;
         int anpos;
         if (!an[i].getScoreOnly())
         {
@@ -2277,7 +2581,9 @@ public class Jalview2XML
             anpos = ae[aa].getPosition();
 
             if (anpos >= anot.length)
+            {
               continue;
+            }
 
             anot[anpos] = new jalview.datamodel.Annotation(
 
@@ -2295,11 +2601,10 @@ public class Jalview2XML
             // {
             // anot[ae[aa].getPosition()].displayCharacter = "";
             // }
-            anot[anpos].colour = new java.awt.Color(
-                    ae[aa].getColour());
-            if (firstColour==null)
+            anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
+            if (firstColour == null)
             {
-              firstColour=anot[anpos].colour;
+              firstColour = anot[anpos].colour;
             }
           }
         }
@@ -2316,7 +2621,7 @@ public class Jalview2XML
                   an[i].getGraphType());
 
           jaa.graphGroup = an[i].getGraphGroup();
-          jaa._linecolour=firstColour;
+          jaa._linecolour = firstColour;
           if (an[i].getThresholdLine() != null)
           {
             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
@@ -2336,7 +2641,7 @@ public class Jalview2XML
         {
           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
                   an[i].getDescription(), anot);
-          jaa._linecolour=firstColour;
+          jaa._linecolour = firstColour;
         }
         // register new annotation
         if (an[i].getId() != null)
@@ -2372,10 +2677,14 @@ public class Jalview2XML
           jaa.setScore(an[i].getScore());
         }
         if (an[i].hasVisible())
+        {
           jaa.visible = an[i].getVisible();
+        }
 
         if (an[i].hasCentreColLabels())
+        {
           jaa.centreColLabels = an[i].getCentreColLabels();
+        }
 
         if (an[i].hasScaleColLabels())
         {
@@ -2397,7 +2706,14 @@ public class Jalview2XML
           jaa.belowAlignment = an[i].isBelowAlignment();
         }
         jaa.setCalcId(an[i].getCalcId());
-
+        if (an[i].getPropertyCount() > 0)
+        {
+          for (jalview.schemabinding.version2.Property prop : an[i]
+                  .getProperty())
+          {
+            jaa.setProperty(prop.getName(), prop.getValue());
+          }
+        }
         if (jaa.autoCalculated)
         {
           autoAlan.add(new JvAnnotRow(i, jaa));
@@ -2411,14 +2727,13 @@ public class Jalview2XML
         }
       }
     }
-
     // ///////////////////////
     // LOAD GROUPS
     // Create alignment markup and styles for this view
     if (jms.getJGroupCount() > 0)
     {
       JGroup[] groups = jms.getJGroup();
-
+      boolean addAnnotSchemeGroup = false;
       for (int i = 0; i < groups.length; i++)
       {
         ColourSchemeI cs = null;
@@ -2427,7 +2742,13 @@ public class Jalview2XML
         {
           if (groups[i].getColour().startsWith("ucs"))
           {
-            cs = GetUserColourScheme(jms, groups[i].getColour());
+            cs = getUserColourScheme(jms, groups[i].getColour());
+          }
+          else if (groups[i].getColour().equals("AnnotationColourGradient")
+                  && groups[i].getAnnotationColours() != null)
+          {
+            addAnnotSchemeGroup = true;
+            cs = null;
           }
           else
           {
@@ -2445,7 +2766,7 @@ public class Jalview2XML
         for (int s = 0; s < groups[i].getSeqCount(); s++)
         {
           String seqId = groups[i].getSeq(s) + "";
-          jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
+          jalview.datamodel.SequenceI ts = seqRefIds
                   .get(seqId);
 
           if (ts != null)
@@ -2480,6 +2801,10 @@ public class Jalview2XML
         {
           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
         }
+        if (groups[i].hasNormaliseSequenceLogo())
+        {
+          sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
+        }
         if (groups[i].hasIgnoreGapsinConsensus())
         {
           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
@@ -2523,10 +2848,19 @@ public class Jalview2XML
           }
         }
         al.addGroup(sg);
-
+        if (addAnnotSchemeGroup)
+        {
+          // reconstruct the annotation colourscheme
+          sg.cs = constructAnnotationColour(
+                  groups[i].getAnnotationColours(), null, al, jms, false);
+        }
       }
     }
-
+    if (view == null)
+    {
+      // only dataset in this model, so just return.
+      return null;
+    }
     // ///////////////////////////////
     // LOAD VIEWPORT
 
@@ -2570,6 +2904,13 @@ public class Jalview2XML
 
       }
     }
+    /**
+     * indicate that annotation colours are applied across all groups (pre
+     * Jalview 2.8.1 behaviour)
+     */
+    boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
+            object.getVersion());
+
     AlignmentPanel ap = null;
     boolean isnewview = true;
     if (viewId != null)
@@ -2597,7 +2938,7 @@ public class Jalview2XML
 
     if (isnewview)
     {
-      af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
+      af = loadViewport(file, jseqs, hiddenSeqs, al, jms, view,
               uniqueSeqSetId, viewId, autoAlan);
       av = af.viewport;
       ap = af.alignPanel;
@@ -2673,365 +3014,590 @@ public class Jalview2XML
           }
         }
 
-      } catch (Exception ex)
+      } catch (Exception ex)
+      {
+        ex.printStackTrace();
+      }
+    }
+
+    // //LOAD STRUCTURES
+    if (loadTreesAndStructures)
+    {
+      loadStructures(jprovider, jseqs, af, ap);
+    }
+    // and finally return.
+    return af;
+  }
+
+  /**
+   * Load and link any saved structure viewers.
+   * 
+   * @param jprovider
+   * @param jseqs
+   * @param af
+   * @param ap
+   */
+  protected void loadStructures(jarInputStreamProvider jprovider,
+          JSeq[] jseqs, AlignFrame af, AlignmentPanel ap)
+  {
+    /*
+     * Run through all PDB ids on the alignment, and collect mappings between
+     * distinct view ids and all sequences referring to that view.
+     */
+    Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<String, StructureViewerModel>();
+
+    for (int i = 0; i < jseqs.length; i++)
+    {
+      if (jseqs[i].getPdbidsCount() > 0)
+      {
+        Pdbids[] ids = jseqs[i].getPdbids();
+        for (int p = 0; p < ids.length; p++)
+        {
+          final int structureStateCount = ids[p].getStructureStateCount();
+          for (int s = 0; s < structureStateCount; s++)
+          {
+            // check to see if we haven't already created this structure view
+            final StructureState structureState = ids[p].getStructureState(s);
+            String sviewid = (structureState.getViewId() == null) ? null
+                    : structureState.getViewId()
+                            + uniqueSetSuffix;
+            jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
+            // Originally : ids[p].getFile()
+            // : TODO: verify external PDB file recovery still works in normal
+            // jalview project load
+            jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
+            jpdb.setId(ids[p].getId());
+
+            int x = structureState.getXpos();
+            int y = structureState.getYpos();
+            int width = structureState.getWidth();
+            int height = structureState.getHeight();
+
+            // Probably don't need to do this anymore...
+            // Desktop.desktop.getComponentAt(x, y);
+            // TODO: NOW: check that this recovers the PDB file correctly.
+            String pdbFile = loadPDBFile(jprovider, ids[p].getId());
+            jalview.datamodel.SequenceI seq = seqRefIds
+                    .get(jseqs[i].getId() + "");
+            if (sviewid == null)
+            {
+              sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
+                      + "," + height;
+            }
+            if (!structureViewers.containsKey(sviewid))
+            {
+              structureViewers.put(sviewid, new StructureViewerModel(x, y, width, height,
+                      false, false, true));
+              // Legacy pre-2.7 conversion JAL-823 :
+              // do not assume any view has to be linked for colour by
+              // sequence
+            }
+
+            // assemble String[] { pdb files }, String[] { id for each
+            // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
+            // seqs_file 2}, boolean[] {
+            // linkAlignPanel,superposeWithAlignpanel}} from hash
+            StructureViewerModel jmoldat = structureViewers.get(sviewid);
+            jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
+                    | (structureState.hasAlignwithAlignPanel() ? structureState
+                            .getAlignwithAlignPanel() : false));
+
+            /*
+             * Default colour by linked panel to false if not specified (e.g.
+             * for pre-2.7 projects)
+             */
+            boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
+            colourWithAlignPanel |= (structureState
+                    .hasColourwithAlignPanel() ? structureState
+                    .getColourwithAlignPanel() : false);
+            jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
+
+            /*
+             * Default colour by viewer to true if not specified (e.g. for
+             * pre-2.7 projects)
+             */
+            boolean colourByViewer = jmoldat.isColourByViewer();
+            colourByViewer &= structureState
+                    .hasColourByJmol() ? structureState
+                    .getColourByJmol() : true;
+            jmoldat.setColourByViewer(colourByViewer);
+
+            if (jmoldat.getStateData().length() < structureState
+                    .getContent().length())
+            {
+              {
+                jmoldat.setStateData(structureState.getContent());
+              }
+            }
+            if (ids[p].getFile() != null)
+            {
+              File mapkey = new File(ids[p].getFile());
+              StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
+              if (seqstrmaps == null)
+              {
+                jmoldat.getFileData().put(
+                        mapkey,
+                        seqstrmaps = jmoldat.new StructureData(pdbFile,
+                                ids[p].getId()));
+              }
+              if (!seqstrmaps.getSeqList().contains(seq))
+              {
+                seqstrmaps.getSeqList().add(seq);
+                // TODO and chains?
+              }
+            }
+            else
+            {
+              errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
+              warn(errorMessage);
+            }
+          }
+        }
+      }
+    }
+      // Instantiate the associated structure views
+      for (Entry<String, StructureViewerModel> entry : structureViewers.entrySet())
+      {
+        createOrLinkStructureViewer(entry, af, ap);
+      }
+  }
+
+  /**
+   * 
+   * @param viewerData
+   * @param af
+   * @param ap
+   */
+  protected void createOrLinkStructureViewer(
+          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+          AlignmentPanel ap)
+  {
+    final StructureViewerModel svattrib = viewerData.getValue();
+
+    /*
+     * Search for any viewer windows already open from other alignment views
+     * that exactly match the stored structure state
+     */
+    StructureViewerBase comp = findMatchingViewer(viewerData);
+
+    if (comp != null)
+    {
+      linkStructureViewer(ap, comp, svattrib);
+      return;
+    }
+
+    /*
+     * Pending an XML element for ViewerType, just check if stateData contains
+     * "chimera" (part of the chimera session filename).
+     */
+    if (svattrib.getStateData().indexOf("chimera") > -1)
+    {
+      createChimeraViewer(viewerData, af);
+    }
+    else
+    {
+      createJmolViewer(viewerData, af);
+    }
+  }
+
+  /**
+   * Create a new Chimera viewer.
+   * 
+   * @param viewerData
+   * @param af
+   */
+  protected void createChimeraViewer(Entry<String, StructureViewerModel> viewerData,
+          AlignFrame af)
+  {
+    final StructureViewerModel data = viewerData.getValue();
+    String chimeraSession = data.getStateData();
+
+    if (new File(chimeraSession).exists())
+    {
+      Set<Entry<File, StructureData>> fileData = data.getFileData()
+              .entrySet();
+      List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
+      List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
+      for (Entry<File, StructureData> pdb : fileData)
+      {
+        String filePath = pdb.getValue().getFilePath();
+        String pdbId = pdb.getValue().getPdbId();
+        pdbs.add(new PDBEntry(filePath, pdbId));
+        final List<SequenceI> seqList = pdb.getValue().getSeqList();
+        SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
+        allseqs.add(seqs);
+      }
+
+      boolean colourByChimera = data.isColourByViewer();
+      boolean colourBySequence = data.isColourWithAlignPanel();
+
+      // TODO can/should this be done via StructureViewer (like Jmol)?
+      final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs
+              .size()]);
+      final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs.size()][]);
+      new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray,
+              seqsArray,
+              colourByChimera, colourBySequence);
+    }
+    else
+    {
+      Cache.log.error("Chimera session file " + chimeraSession
+              + " not found");
+    }
+  }
+
+  /**
+   * Create a new Jmol window. First parse the Jmol state to translate filenames
+   * loaded into the view, and record the order in which files are shown in the
+   * Jmol view, so we can add the sequence mappings in same order.
+   * 
+   * @param viewerData
+   * @param af
+   */
+  protected void createJmolViewer(
+          final Entry<String, StructureViewerModel> viewerData, AlignFrame af)
+  {
+    final StructureViewerModel svattrib = viewerData.getValue();
+    String state = svattrib.getStateData();
+    List<String> pdbfilenames = new ArrayList<String>();
+    List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
+    List<String> pdbids = new ArrayList<String>();
+    StringBuilder newFileLoc = new StringBuilder(64);
+    int cp = 0, ncp, ecp;
+    Map<File, StructureData> oldFiles = svattrib.getFileData();
+    while ((ncp = state.indexOf("load ", cp)) > -1)
+    {
+      do
+      {
+        // look for next filename in load statement
+        newFileLoc.append(state.substring(cp,
+                ncp = (state.indexOf("\"", ncp + 1) + 1)));
+        String oldfilenam = state.substring(ncp,
+                ecp = state.indexOf("\"", ncp));
+        // recover the new mapping data for this old filename
+        // have to normalize filename - since Jmol and jalview do
+        // filename
+        // translation differently.
+        StructureData filedat = oldFiles.get(new File(oldfilenam));
+        newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
+        pdbfilenames.add(filedat.getFilePath());
+        pdbids.add(filedat.getPdbId());
+        seqmaps.add(filedat.getSeqList()
+                .toArray(new SequenceI[0]));
+        newFileLoc.append("\"");
+        cp = ecp + 1; // advance beyond last \" and set cursor so we can
+                      // look for next file statement.
+      } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
+    }
+    if (cp > 0)
+    {
+      // just append rest of state
+      newFileLoc.append(state.substring(cp));
+    }
+    else
+    {
+      System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
+      newFileLoc = new StringBuilder(state);
+      newFileLoc.append("; load append ");
+      for (File id : oldFiles.keySet())
+      {
+        // add this and any other pdb files that should be present in
+        // the viewer
+        StructureData filedat = oldFiles.get(id);
+        newFileLoc.append(filedat.getFilePath());
+        pdbfilenames.add(filedat.getFilePath());
+        pdbids.add(filedat.getPdbId());
+        seqmaps.add(filedat.getSeqList()
+                .toArray(new SequenceI[0]));
+        newFileLoc.append(" \"");
+        newFileLoc.append(filedat.getFilePath());
+        newFileLoc.append("\"");
+
+      }
+      newFileLoc.append(";");
+    }
+
+    if (newFileLoc.length() > 0)
+    {
+      int histbug = newFileLoc.indexOf("history = ");
+      histbug += 10;
+      int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
+      String val = (diff == -1) ? null : newFileLoc
+              .substring(histbug, diff);
+      if (val != null && val.length() >= 4)
+      {
+        if (val.contains("e"))
+        {
+          if (val.trim().equals("true"))
+          {
+            val = "1";
+          }
+          else
+          {
+            val = "0";
+          }
+          newFileLoc.replace(histbug, diff, val);
+        }
+      }
+
+      final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
+              .size()]);
+      final String[] id = pdbids.toArray(new String[pdbids.size()]);
+      final SequenceI[][] sq = seqmaps
+              .toArray(new SequenceI[seqmaps.size()][]);
+      final String fileloc = newFileLoc.toString();
+      final String sviewid = viewerData.getKey();
+      final AlignFrame alf = af;
+      final Rectangle rect = new Rectangle(svattrib.getX(),
+              svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
+      try
+      {
+        javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            JalviewStructureDisplayI sview = null;
+            try
+            {
+              // JAL-1333 note - we probably can't migrate Jmol views to UCSF
+              // Chimera!
+              sview = new StructureViewer(alf.alignPanel
+                      .getStructureSelectionManager()).createView(
+                      StructureViewer.ViewerType.JMOL, pdbf, id, sq,
+                      alf.alignPanel, svattrib, fileloc, rect, sviewid);
+              addNewStructureViewer(sview);
+            } catch (OutOfMemoryError ex)
+            {
+              new OOMWarning("restoring structure view for PDB id " + id,
+                      (OutOfMemoryError) ex.getCause());
+              if (sview != null && sview.isVisible())
+              {
+                sview.closeViewer();
+                sview.setVisible(false);
+                sview.dispose();
+              }
+            }
+          }
+        });
+      } catch (InvocationTargetException ex)
+      {
+        warn("Unexpected error when opening Jmol view.", ex);
+
+      } catch (InterruptedException e)
+      {
+        // e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * Returns any open frame that matches given structure viewer data. The match
+   * is based on the unique viewId, or (for older project versions) the frame's
+   * geometry.
+   * 
+   * @param viewerData
+   * @return
+   */
+  protected StructureViewerBase findMatchingViewer(
+          Entry<String, StructureViewerModel> viewerData)
+  {
+    final String sviewid = viewerData.getKey();
+    final StructureViewerModel svattrib = viewerData.getValue();
+    StructureViewerBase comp = null;
+    JInternalFrame[] frames = getAllFrames();
+    for (JInternalFrame frame : frames)
+    {
+      if (frame instanceof StructureViewerBase)
+      {
+        /*
+         * Post jalview 2.4 schema includes structure view id
+         */
+        if (sviewid != null
+                && ((StructureViewerBase) frame).getViewId().equals(
+                        sviewid))
+        {
+          comp = (AppJmol) frame;
+          // todo: break?
+        }
+        /*
+         * Otherwise test for matching position and size of viewer frame
+         */
+        else if (frame.getX() == svattrib.getX()
+                && frame.getY() == svattrib.getY()
+                && frame.getHeight() == svattrib.getHeight()
+                && frame.getWidth() == svattrib.getWidth())
+        {
+          comp = (AppJmol) frame;
+          // todo: break?
+        }
+      }
+    }
+    return comp;
+  }
+
+  /**
+   * Link an AlignmentPanel to an existing structure viewer.
+   * 
+   * @param ap
+   * @param viewer
+   * @param oldFiles
+   * @param useinViewerSuperpos
+   * @param usetoColourbyseq
+   * @param viewerColouring
+   */
+  protected void linkStructureViewer(AlignmentPanel ap,
+          StructureViewerBase viewer, StructureViewerModel svattrib)
+  {
+    // NOTE: if the jalview project is part of a shared session then
+    // view synchronization should/could be done here.
+
+    final boolean useinViewerSuperpos = svattrib.isAlignWithPanel();
+    final boolean usetoColourbyseq = svattrib.isColourWithAlignPanel();
+    final boolean viewerColouring = svattrib.isColourByViewer();
+    Map<File, StructureData> oldFiles = svattrib.getFileData();
+
+    /*
+     * Add mapping for sequences in this view to an already open viewer
+     */
+    final AAStructureBindingModel binding = viewer.getBinding();
+    for (File id : oldFiles.keySet())
+    {
+      // add this and any other pdb files that should be present in the
+      // viewer
+      StructureData filedat = oldFiles.get(id);
+      String pdbFile = filedat.getFilePath();
+      SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
+      binding.getSsm().setMapping(seq, null, pdbFile,
+              jalview.io.AppletFormatAdapter.FILE);
+      binding.addSequenceForStructFile(pdbFile, seq);
+    }
+    // and add the AlignmentPanel's reference to the view panel
+    viewer.addAlignmentPanel(ap);
+    if (useinViewerSuperpos)
+    {
+      viewer.useAlignmentPanelForSuperposition(ap);
+    }
+    else
+    {
+      viewer.excludeAlignmentPanelForSuperposition(ap);
+    }
+    if (usetoColourbyseq)
+    {
+      viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
+    }
+    else
+    {
+      viewer.excludeAlignmentPanelForColourbyseq(ap);
+    }
+  }
+
+  /**
+   * Get all frames within the Desktop.
+   * 
+   * @return
+   */
+  protected JInternalFrame[] getAllFrames()
+  {
+    JInternalFrame[] frames = null;
+    // TODO is this necessary - is it safe - risk of hanging?
+    do
+    {
+      try
       {
-        ex.printStackTrace();
+        frames = Desktop.desktop.getAllFrames();
+      } catch (ArrayIndexOutOfBoundsException e)
+      {
+        // occasional No such child exceptions are thrown here...
+        try
+        {
+          Thread.sleep(10);
+        } catch (InterruptedException f)
+        {
+        }
       }
-    }
+    } while (frames == null);
+    return frames;
+  }
 
-    // //LOAD STRUCTURES
-    if (loadTreesAndStructures)
+  /**
+   * 
+   * @param supported
+   *          - minimum version we are comparing against
+   * @param version
+   *          - version of data being processsed.
+   * @return true if version is development/null or evaluates to the same or
+   *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
+   */
+  private boolean isVersionStringLaterThan(String supported, String version)
+  {
+    if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
+            || version.equalsIgnoreCase("Test")
+            || version.equalsIgnoreCase("AUTOMATED BUILD"))
     {
-      // run through all PDB ids on the alignment, and collect mappings between
-      // jmol view ids and all sequences referring to it
-      Hashtable<String, Object[]> jmolViewIds = new Hashtable();
-
-      for (int i = 0; i < JSEQ.length; i++)
+      System.err.println("Assuming project file with "
+              + (version == null ? "null" : version)
+              + " is compatible with Jalview version " + supported);
+      return true;
+    }
+    else
+    {
+      StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
+              version, ".");
+      while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
       {
-        if (JSEQ[i].getPdbidsCount() > 0)
+        // convert b to decimal to catch bugfix releases within a series
+        String curT = currentV.nextToken().toLowerCase().replace('b', '.');
+        String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
+        try
         {
-          Pdbids[] ids = JSEQ[i].getPdbids();
-          for (int p = 0; p < ids.length; p++)
+          if (Float.valueOf(curT) > Float.valueOf(fileT))
           {
-            for (int s = 0; s < ids[p].getStructureStateCount(); s++)
-            {
-              // check to see if we haven't already created this structure view
-              String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
-                      : ids[p].getStructureState(s).getViewId()
-                              + uniqueSetSuffix;
-              jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
-              // Originally : ids[p].getFile()
-              // : TODO: verify external PDB file recovery still works in normal
-              // jalview project load
-              jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
-              jpdb.setId(ids[p].getId());
-
-              int x = ids[p].getStructureState(s).getXpos();
-              int y = ids[p].getStructureState(s).getYpos();
-              int width = ids[p].getStructureState(s).getWidth();
-              int height = ids[p].getStructureState(s).getHeight();
-
-              // Probably don't need to do this anymore...
-              // Desktop.desktop.getComponentAt(x, y);
-              // TODO: NOW: check that this recovers the PDB file correctly.
-              String pdbFile = loadPDBFile(jprovider, ids[p].getId());
-              jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
-                      .get(JSEQ[i].getId() + "");
-              if (sviewid == null)
-              {
-                sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
-                        + "," + height;
-              }
-              if (!jmolViewIds.containsKey(sviewid))
-              {
-                jmolViewIds.put(sviewid, new Object[]
-                { new int[]
-                { x, y, width, height }, "",
-                    new Hashtable<String, Object[]>(), new boolean[]
-                    { false, false, true } });
-                // Legacy pre-2.7 conversion JAL-823 :
-                // do not assume any view has to be linked for colour by
-                // sequence
-              }
-
-              // assemble String[] { pdb files }, String[] { id for each
-              // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
-              // seqs_file 2}, boolean[] {
-              // linkAlignPanel,superposeWithAlignpanel}} from hash
-              Object[] jmoldat = jmolViewIds.get(sviewid);
-              ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
-                      .hasAlignwithAlignPanel() ? ids[p].getStructureState(
-                      s).getAlignwithAlignPanel() : false;
-              // never colour by linked panel if not specified
-              ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
-                      .hasColourwithAlignPanel() ? ids[p]
-                      .getStructureState(s).getColourwithAlignPanel()
-                      : false;
-              // default for pre-2.7 projects is that Jmol colouring is enabled
-              ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
-                      .hasColourByJmol() ? ids[p].getStructureState(s)
-                      .getColourByJmol() : true;
-
-              if (((String) jmoldat[1]).length() < ids[p]
-                      .getStructureState(s).getContent().length())
-              {
-                {
-                  jmoldat[1] = ids[p].getStructureState(s).getContent();
-                }
-              }
-              if (ids[p].getFile() != null)
-              {
-                File mapkey = new File(ids[p].getFile());
-                Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
-                        .get(mapkey);
-                if (seqstrmaps == null)
-                {
-                  ((Hashtable) jmoldat[2]).put(mapkey,
-                          seqstrmaps = new Object[]
-                          { pdbFile, ids[p].getId(), new Vector(),
-                              new Vector() });
-                }
-                if (!((Vector) seqstrmaps[2]).contains(seq))
-                {
-                  ((Vector) seqstrmaps[2]).addElement(seq);
-                  // ((Vector)seqstrmaps[3]).addElement(n) :
-                  // in principle, chains
-                  // should be stored here : do we need to
-                  // TODO: store and recover seq/pdb_id :
-                  // chain mappings
-                }
-              }
-              else
-              {
-                errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
-                warn(errorMessage);
-              }
-            }
+            // current version is newer than the version that wrote the file
+            return false;
           }
+        } catch (NumberFormatException nfe)
+        {
+          System.err
+                  .println("** WARNING: Version comparison failed for tokens ("
+                          + curT
+                          + ") and ("
+                          + fileT
+                          + ")\n** Current: '"
+                          + supported + "' and Version: '" + version + "'");
         }
       }
+      if (currentV.hasMoreElements())
       {
+        // fileV has no minor version but identical series to current
+        return false;
+      }
+    }
+    return true;
+  }
 
-        // Instantiate the associated Jmol views
-        for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
-        {
-          String sviewid = entry.getKey();
-          Object[] svattrib = entry.getValue();
-          int[] geom = (int[]) svattrib[0];
-          String state = (String) svattrib[1];
-          Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
-          final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
-          int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
-          // collate the pdbfile -> sequence mappings from this view
-          Vector<String> pdbfilenames = new Vector<String>();
-          Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
-          Vector<String> pdbids = new Vector<String>();
-
-          // Search to see if we've already created this Jmol view
-          AppJmol comp = null;
-          JInternalFrame[] frames = null;
-          do
-          {
-            try
-            {
-              frames = Desktop.desktop.getAllFrames();
-            } catch (ArrayIndexOutOfBoundsException e)
-            {
-              // occasional No such child exceptions are thrown here...
-              frames = null;
-              try
-              {
-                Thread.sleep(10);
-              } catch (Exception f)
-              {
-              }
-              ;
-            }
-          } while (frames == null);
-          // search for any Jmol windows already open from other
-          // alignment views that exactly match the stored structure state
-          for (int f = 0; comp == null && f < frames.length; f++)
-          {
-            if (frames[f] instanceof AppJmol)
-            {
-              if (sviewid != null
-                      && ((AppJmol) frames[f]).getViewId().equals(sviewid))
-              {
-                // post jalview 2.4 schema includes structure view id
-                comp = (AppJmol) frames[f];
-              }
-              else if (frames[f].getX() == x && frames[f].getY() == y
-                      && frames[f].getHeight() == height
-                      && frames[f].getWidth() == width)
-              {
-                comp = (AppJmol) frames[f];
-              }
-            }
-          }
-
-          if (comp == null)
-          {
-            // create a new Jmol window.
-            // First parse the Jmol state to translate filenames loaded into the
-            // view, and record the order in which files are shown in the Jmol
-            // view, so we can add the sequence mappings in same order.
-            StringBuffer newFileLoc = null;
-            int cp = 0, ncp, ecp;
-            while ((ncp = state.indexOf("load ", cp)) > -1)
-            {
-              if (newFileLoc == null)
-              {
-                newFileLoc = new StringBuffer();
-              }
-              do
-              {
-                // look for next filename in load statement
-                newFileLoc.append(state.substring(cp,
-                        ncp = (state.indexOf("\"", ncp + 1) + 1)));
-                String oldfilenam = state.substring(ncp,
-                        ecp = state.indexOf("\"", ncp));
-                // recover the new mapping data for this old filename
-                // have to normalize filename - since Jmol and jalview do
-                // filename
-                // translation differently.
-                Object[] filedat = oldFiles.get(new File(oldfilenam));
-                newFileLoc.append(Platform
-                        .escapeString((String) filedat[0]));
-                pdbfilenames.addElement((String) filedat[0]);
-                pdbids.addElement((String) filedat[1]);
-                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
-                        .toArray(new SequenceI[0]));
-                newFileLoc.append("\"");
-                cp = ecp + 1; // advance beyond last \" and set cursor so we can
-                              // look for next file statement.
-              } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
-            }
-            if (cp > 0)
-            {
-              // just append rest of state
-              newFileLoc.append(state.substring(cp));
-            }
-            else
-            {
-              System.err
-                      .print("Ignoring incomplete Jmol state for PDB ids: ");
-              newFileLoc = new StringBuffer(state);
-              newFileLoc.append("; load append ");
-              for (File id : oldFiles.keySet())
-              {
-                // add this and any other pdb files that should be present in
-                // the viewer
-                Object[] filedat = oldFiles.get(id);
-                String nfilename;
-                newFileLoc.append(((String) filedat[0]));
-                pdbfilenames.addElement((String) filedat[0]);
-                pdbids.addElement((String) filedat[1]);
-                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
-                        .toArray(new SequenceI[0]));
-                newFileLoc.append(" \"");
-                newFileLoc.append((String) filedat[0]);
-                newFileLoc.append("\"");
-
-              }
-              newFileLoc.append(";");
-            }
-
-            if (newFileLoc != null)
-            {
-              int histbug = newFileLoc.indexOf("history = ");
-              histbug += 10;
-              int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
-                      histbug);
-              String val = (diff == -1) ? null : newFileLoc.substring(
-                      histbug, diff);
-              if (val != null && val.length() >= 4)
-              {
-                if (val.contains("e"))
-                {
-                  if (val.trim().equals("true"))
-                  {
-                    val = "1";
-                  }
-                  else
-                  {
-                    val = "0";
-                  }
-                  newFileLoc.replace(histbug, diff, val);
-                }
-              }
-              // TODO: assemble String[] { pdb files }, String[] { id for each
-              // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
-              // seqs_file 2}} from hash
-              final String[] pdbf = pdbfilenames
-                      .toArray(new String[pdbfilenames.size()]), id = pdbids
-                      .toArray(new String[pdbids.size()]);
-              final SequenceI[][] sq = seqmaps
-                      .toArray(new SequenceI[seqmaps.size()][]);
-              final String fileloc = newFileLoc.toString(), vid = sviewid;
-              final AlignFrame alf = af;
-              final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
-                      width, height);
-              try
-              {
-                javax.swing.SwingUtilities.invokeAndWait(new Runnable()
-                {
-                  @Override
-                  public void run()
-                  {
-                    AppJmol sview = null;
-                    try
-                    {
-                      sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
-                              useinJmolsuperpos, usetoColourbyseq,
-                              jmolColouring, fileloc, rect, vid);
-                    } catch (OutOfMemoryError ex)
-                    {
-                      new OOMWarning("restoring structure view for PDB id "
-                              + id, (OutOfMemoryError) ex.getCause());
-                      if (sview != null && sview.isVisible())
-                      {
-                        sview.closeViewer();
-                        sview.setVisible(false);
-                        sview.dispose();
-                      }
-                    }
-                  }
-                });
-              } catch (InvocationTargetException ex)
-              {
-                warn("Unexpected error when opening Jmol view.", ex);
-
-              } catch (InterruptedException e)
-              {
-                // e.printStackTrace();
-              }
-            }
+  Vector<JalviewStructureDisplayI> newStructureViewers = null;
 
-          }
-          else
-          // if (comp != null)
-          {
-            // NOTE: if the jalview project is part of a shared session then
-            // view synchronization should/could be done here.
+  protected void addNewStructureViewer(JalviewStructureDisplayI sview)
+  {
+    if (newStructureViewers != null)
+    {
+      sview.getBinding().setFinishedLoadingFromArchive(false);
+      newStructureViewers.add(sview);
+    }
+  }
 
-            // add mapping for sequences in this view to an already open Jmol
-            // instance
-            for (File id : oldFiles.keySet())
-            {
-              // add this and any other pdb files that should be present in the
-              // viewer
-              Object[] filedat = oldFiles.get(id);
-              String pdbFile = (String) filedat[0];
-              SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
-                      .toArray(new SequenceI[0]);
-              comp.jmb.ssm.setMapping(seq, null, pdbFile,
-                      jalview.io.AppletFormatAdapter.FILE);
-              comp.jmb.addSequenceForStructFile(pdbFile, seq);
-            }
-            // and add the AlignmentPanel's reference to the Jmol view
-            comp.addAlignmentPanel(ap);
-            if (useinJmolsuperpos)
-            {
-              comp.useAlignmentPanelForSuperposition(ap);
-            }
-            else
-            {
-              comp.excludeAlignmentPanelForSuperposition(ap);
-            }
-            if (usetoColourbyseq)
-            {
-              comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
-            }
-            else
-            {
-              comp.excludeAlignmentPanelForColourbyseq(ap);
-            }
-          }
-        }
+  protected void setLoadingFinishedForNewStructureViewers()
+  {
+    if (newStructureViewers != null)
+    {
+      for (JalviewStructureDisplayI sview : newStructureViewers)
+      {
+        sview.getBinding().setFinishedLoadingFromArchive(true);
       }
+      newStructureViewers.clear();
+      newStructureViewers = null;
     }
-    // and finally return.
-    return af;
   }
 
   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
@@ -3115,7 +3681,7 @@ public class Jalview2XML
 
     af.viewport.setConservationSelected(view.getConservationSelected());
     af.viewport.setShowJVSuffix(view.getShowFullId());
-    af.viewport.rightAlignIds = view.getRightAlignIds();
+    af.viewport.setRightAlignIds(view.getRightAlignIds());
     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
             .getFontStyle(), view.getFontSize()));
     af.alignPanel.fontChanged();
@@ -3143,97 +3709,15 @@ public class Jalview2XML
     {
       if (view.getBgColour().startsWith("ucs"))
       {
-        cs = GetUserColourScheme(jms, view.getBgColour());
+        cs = getUserColourScheme(jms, view.getBgColour());
       }
       else if (view.getBgColour().startsWith("Annotation"))
       {
-        // int find annotation
-        if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
-        {
-          for (int i = 0; i < af.viewport.getAlignment()
-                  .getAlignmentAnnotation().length; i++)
-          {
-            if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
-                    .equals(view.getAnnotationColours().getAnnotation()))
-            {
-              if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
-                      .getThreshold() == null)
-              {
-                af.viewport.getAlignment().getAlignmentAnnotation()[i]
-                        .setThreshold(new jalview.datamodel.GraphLine(view
-                                .getAnnotationColours().getThreshold(),
-                                "Threshold", java.awt.Color.black)
-
-                        );
-              }
-
-              if (view.getAnnotationColours().getColourScheme()
-                      .equals("None"))
-              {
-                cs = new AnnotationColourGradient(af.viewport
-                        .getAlignment().getAlignmentAnnotation()[i],
-                        new java.awt.Color(view.getAnnotationColours()
-                                .getMinColour()), new java.awt.Color(view
-                                .getAnnotationColours().getMaxColour()),
-                        view.getAnnotationColours().getAboveThreshold());
-              }
-              else if (view.getAnnotationColours().getColourScheme()
-                      .startsWith("ucs"))
-              {
-                cs = new AnnotationColourGradient(af.viewport
-                        .getAlignment().getAlignmentAnnotation()[i],
-                        GetUserColourScheme(jms, view
-                                .getAnnotationColours().getColourScheme()),
-                        view.getAnnotationColours().getAboveThreshold());
-              }
-              else
-              {
-                cs = new AnnotationColourGradient(af.viewport
-                        .getAlignment().getAlignmentAnnotation()[i],
-                        ColourSchemeProperty.getColour(al, view
-                                .getAnnotationColours().getColourScheme()),
-                        view.getAnnotationColours().getAboveThreshold());
-              }
-
-              // Also use these settings for all the groups
-              if (al.getGroups() != null)
-              {
-                for (int g = 0; g < al.getGroups().size(); g++)
-                {
-                  jalview.datamodel.SequenceGroup sg = al.getGroups()
-                          .get(g);
-
-                  if (sg.cs == null)
-                  {
-                    continue;
-                  }
-
-                  /*
-                   * if
-                   * (view.getAnnotationColours().getColourScheme().equals("None"
-                   * )) { sg.cs = new AnnotationColourGradient(
-                   * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
-                   * java.awt.Color(view.getAnnotationColours().
-                   * getMinColour()), new
-                   * java.awt.Color(view.getAnnotationColours().
-                   * getMaxColour()),
-                   * view.getAnnotationColours().getAboveThreshold()); } else
-                   */
-                  {
-                    sg.cs = new AnnotationColourGradient(af.viewport
-                            .getAlignment().getAlignmentAnnotation()[i],
-                            sg.cs, view.getAnnotationColours()
-                                    .getAboveThreshold());
-                  }
-
-                }
-              }
+        AnnotationColours viewAnnColour = view.getAnnotationColours();
+        cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
 
-              break;
-            }
+        // annpos
 
-          }
-        }
       }
       else
       {
@@ -3259,10 +3743,8 @@ public class Jalview2XML
 
     af.viewport.setColourAppliesToAllGroups(true);
 
-    if (view.getShowSequenceFeatures())
-    {
-      af.viewport.showSequenceFeatures = true;
-    }
+    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
+
     if (view.hasCentreColumnLabels())
     {
       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
@@ -3297,6 +3779,10 @@ public class Jalview2XML
     {
       af.viewport.setShowSequenceLogo(false);
     }
+    if (view.hasNormaliseSequenceLogo())
+    {
+      af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
+    }
     if (view.hasShowDbRefTooltip())
     {
       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
@@ -3325,9 +3811,14 @@ public class Jalview2XML
     // recover featre settings
     if (jms.getFeatureSettings() != null)
     {
-      af.viewport.featuresDisplayed = new Hashtable();
+      FeaturesDisplayed fdi;
+      af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
+      Hashtable featureGroups = new Hashtable();
+      Hashtable featureColours = new Hashtable();
+      Hashtable featureOrder = new Hashtable();
+
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
@@ -3354,37 +3845,42 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(), gc);
+          featureColours.put(setting.getType(), gc);
         }
         else
         {
-          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
-                  setting.getType(),
+          featureColours.put(setting.getType(),
                   new java.awt.Color(setting.getColour()));
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
-          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(), setting.getOrder());
+        {
+          featureOrder.put(setting.getType(), setting.getOrder());
+        }
         else
-          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
-                  setting.getType(),
-                  fs / jms.getFeatureSettings().getSettingCount());
+        {
+          featureOrder.put(setting.getType(), new Float(fs
+                  / jms.getFeatureSettings().getSettingCount()));
+        }
         if (setting.getDisplay())
         {
-          af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
-                  setting.getColour()));
+          fdi.setVisible(setting.getType());
         }
       }
-      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
-      Hashtable fgtable;
-      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
+      Hashtable fgtable = new Hashtable();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
       }
+      // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
+      // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
+      // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
+      FeatureRendererSettings frs = new FeatureRendererSettings(
+              renderOrder, fgtable, featureColours, 1.0f, featureOrder);
+      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(frs);
+
     }
 
     if (view.getHiddenColumnsCount() > 0)
@@ -3417,11 +3913,137 @@ public class Jalview2XML
     // TODO: we don't need to do this if the viewport is aready visible.
     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
             view.getHeight());
-    af.alignPanel.updateAnnotation(false); // recompute any autoannotation
+    af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
     reorderAutoannotation(af, al, autoAlan);
+    af.alignPanel.alignmentChanged();
     return af;
   }
 
+  private ColourSchemeI constructAnnotationColour(
+          AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+          JalviewModelSequence jms, boolean checkGroupAnnColour)
+  {
+    boolean propagateAnnColour = false;
+    ColourSchemeI cs = null;
+    AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
+    if (checkGroupAnnColour && al.getGroups() != null
+            && al.getGroups().size() > 0)
+    {
+      // pre 2.8.1 behaviour
+      // check to see if we should transfer annotation colours
+      propagateAnnColour = true;
+      for (jalview.datamodel.SequenceGroup sg : al.getGroups())
+      {
+        if (sg.cs instanceof AnnotationColourGradient)
+        {
+          propagateAnnColour = false;
+        }
+      }
+    }
+    // int find annotation
+    if (annAlignment.getAlignmentAnnotation() != null)
+    {
+      for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
+      {
+        if (annAlignment.getAlignmentAnnotation()[i].label
+                .equals(viewAnnColour.getAnnotation()))
+        {
+          if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
+          {
+            annAlignment.getAlignmentAnnotation()[i]
+                    .setThreshold(new jalview.datamodel.GraphLine(
+                            viewAnnColour.getThreshold(), "Threshold",
+                            java.awt.Color.black)
+
+                    );
+          }
+
+          if (viewAnnColour.getColourScheme().equals("None"))
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    new java.awt.Color(viewAnnColour.getMinColour()),
+                    new java.awt.Color(viewAnnColour.getMaxColour()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          else if (viewAnnColour.getColourScheme().startsWith("ucs"))
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    getUserColourScheme(jms,
+                            viewAnnColour.getColourScheme()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          else
+          {
+            cs = new AnnotationColourGradient(
+                    annAlignment.getAlignmentAnnotation()[i],
+                    ColourSchemeProperty.getColour(al,
+                            viewAnnColour.getColourScheme()),
+                    viewAnnColour.getAboveThreshold());
+          }
+          if (viewAnnColour.hasPerSequence())
+          {
+            ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
+                    .isPerSequence());
+          }
+          if (viewAnnColour.hasPredefinedColours())
+          {
+            ((AnnotationColourGradient) cs)
+                    .setPredefinedColours(viewAnnColour
+                            .isPredefinedColours());
+          }
+          if (propagateAnnColour && al.getGroups() != null)
+          {
+            // Also use these settings for all the groups
+            for (int g = 0; g < al.getGroups().size(); g++)
+            {
+              jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
+
+              if (sg.cs == null)
+              {
+                continue;
+              }
+
+              /*
+               * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
+               * new AnnotationColourGradient(
+               * annAlignment.getAlignmentAnnotation()[i], new
+               * java.awt.Color(viewAnnColour. getMinColour()), new
+               * java.awt.Color(viewAnnColour. getMaxColour()),
+               * viewAnnColour.getAboveThreshold()); } else
+               */
+              {
+                sg.cs = new AnnotationColourGradient(
+                        annAlignment.getAlignmentAnnotation()[i], sg.cs,
+                        viewAnnColour.getAboveThreshold());
+                if (cs instanceof AnnotationColourGradient)
+                {
+                  if (viewAnnColour.hasPerSequence())
+                  {
+                    ((AnnotationColourGradient) cs)
+                            .setSeqAssociated(viewAnnColour.isPerSequence());
+                  }
+                  if (viewAnnColour.hasPredefinedColours())
+                  {
+                    ((AnnotationColourGradient) cs)
+                            .setPredefinedColours(viewAnnColour
+                                    .isPredefinedColours());
+                  }
+                }
+              }
+
+            }
+          }
+
+          break;
+        }
+
+      }
+    }
+    return cs;
+  }
+
   private void reorderAutoannotation(AlignFrame af, Alignment al,
           ArrayList<JvAnnotRow> autoAlan)
   {
@@ -3525,7 +4147,7 @@ public class Jalview2XML
 
   /**
    * TODO remove this method
-   *
+   * 
    * @param view
    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
@@ -3536,7 +4158,7 @@ public class Jalview2XML
 
   /**
    * Check if the Jalview view contained in object should be skipped or not.
-   *
+   * 
    * @param object
    * @return true if view's sequenceSetId is a key in skipList
    */
@@ -3559,7 +4181,7 @@ public class Jalview2XML
     return false;
   }
 
-  public void AddToSkipList(AlignFrame af)
+  public void addToSkipList(AlignFrame af)
   {
     if (skipList == null)
     {
@@ -3577,7 +4199,8 @@ public class Jalview2XML
     }
   }
 
-  private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
+  private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
+          boolean ignoreUnrefed)
   {
     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
     Vector dseqs = null;
@@ -3589,7 +4212,7 @@ public class Jalview2XML
     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
     {
       Sequence vamsasSeq = vamsasSet.getSequence(i);
-      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
+      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
     }
     // create a new dataset
     if (ds == null)
@@ -3602,14 +4225,14 @@ public class Jalview2XML
       addDatasetRef(vamsasSet.getDatasetId(), ds);
     }
     // set the dataset for the newly imported alignment.
-    if (al.getDataset() == null)
+    if (al.getDataset() == null && !ignoreUnrefed)
     {
       al.setDataset(ds);
     }
   }
 
   /**
-   *
+   * 
    * @param vamsasSeq
    *          sequence definition to create/merge dataset sequence for
    * @param ds
@@ -3618,7 +4241,7 @@ public class Jalview2XML
    *          vector to add new dataset sequence to
    */
   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-          AlignmentI ds, Vector dseqs)
+          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
@@ -3629,14 +4252,17 @@ public class Jalview2XML
     {
       dsq = sq.getDatasetSequence();
     }
-
+    if (sq == null && ignoreUnrefed)
+    {
+      return;
+    }
     String sqid = vamsasSeq.getDsseqid();
     if (dsq == null)
     {
       // need to create or add a new dataset sequence reference to this sequence
       if (sqid != null)
       {
-        dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
+        dsq = seqRefIds.get(sqid);
       }
       // check again
       if (dsq == null)
@@ -3667,6 +4293,24 @@ public class Jalview2XML
         if (sq != dsq)
         { // make this dataset sequence sq's dataset sequence
           sq.setDatasetSequence(dsq);
+          // and update the current dataset alignment
+          if (ds == null)
+          {
+            if (dseqs != null)
+            {
+              if (!dseqs.contains(dsq))
+              {
+                dseqs.add(dsq);
+              }
+            }
+            else
+            {
+              if (ds.findIndex(dsq) < 0)
+              {
+                ds.addSequence(dsq);
+              }
+            }
+          }
         }
       }
     }
@@ -3693,11 +4337,12 @@ public class Jalview2XML
            * sb.append(newres.substring(newres.length() - sq.getEnd() -
            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
            */
-          dsq.setSequence(sb.toString());
+          dsq.setSequence(newres);
         }
         // TODO: merges will never happen if we 'know' we have the real dataset
         // sequence - this should be detected when id==dssid
-        System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
+        System.err
+                .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
         // + (pre ? "prepended" : "") + " "
         // + (post ? "appended" : ""));
       }
@@ -3733,7 +4378,7 @@ public class Jalview2XML
 
   /**
    * make a new dataset ID for this jalview dataset alignment
-   *
+   * 
    * @param dataset
    * @return
    */
@@ -3813,7 +4458,7 @@ public class Jalview2XML
           /**
            * recover from hash
            */
-          jmap.setTo((SequenceI) seqRefIds.get(dsfor));
+          jmap.setTo(seqRefIds.get(dsfor));
         }
         else
         {
@@ -3872,7 +4517,7 @@ public class Jalview2XML
           boolean keepSeqRefs)
   {
     initSeqRefs();
-    jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
+    jalview.schemabinding.version2.JalviewModel jm = saveState(ap, null,
             null);
 
     if (!keepSeqRefs)
@@ -3895,7 +4540,7 @@ public class Jalview2XML
 
     viewportsAdded = new Hashtable();
 
-    AlignFrame af = LoadFromObject(jm, null, false, null);
+    AlignFrame af = loadFromObject(jm, null, false, null);
     af.alignPanels.clear();
     af.closeMenuItem_actionPerformed(true);
 
@@ -3920,7 +4565,7 @@ public class Jalview2XML
 
   /*
    * (non-Javadoc)
-   *
+   * 
    * @see java.lang.Object#finalize()
    */
   @Override
@@ -3998,13 +4643,13 @@ public class Jalview2XML
    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
    * object goes out of scope. - also populates the datasetIds hashtable with
    * alignment objects containing dataset sequences
-   *
+   * 
    * @param vobj2jv
    *          Map from ID strings to jalview datamodel
    * @param jv2vobj
    *          Map from jalview datamodel to ID strings
-   *
-   *
+   * 
+   * 
    */
   public void setObjectMappingTables(Hashtable vobj2jv,
           IdentityHashMap jv2vobj)
@@ -4029,14 +4674,14 @@ public class Jalview2XML
         // register sequence object so the XML parser can recover it.
         if (seqRefIds == null)
         {
-          seqRefIds = new Hashtable();
+          seqRefIds = new HashMap<String, SequenceI>();
         }
         if (seqsToIds == null)
         {
-          seqsToIds = new IdentityHashMap();
+          seqsToIds = new IdentityHashMap<SequenceI, String>();
         }
-        seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
-        seqsToIds.put(jvobj, id);
+        seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
+        seqsToIds.put((SequenceI) jvobj, id);
       }
       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
       {
@@ -4068,7 +4713,9 @@ public class Jalview2XML
         }
       }
       else
+      {
         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
+      }
     }
   }
 
@@ -4076,7 +4723,7 @@ public class Jalview2XML
    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
    * objects created from the project archive. If string is null (default for
    * construction) then suffix will be set automatically.
-   *
+   * 
    * @param string
    */
   public void setUniqueSetSuffix(String string)
@@ -4088,12 +4735,11 @@ public class Jalview2XML
   /**
    * uses skipList2 as the skipList for skipping views on sequence sets
    * associated with keys in the skipList
-   *
+   * 
    * @param skipList2
    */
   public void setSkipList(Hashtable skipList2)
   {
     skipList = skipList2;
   }
-
 }