JAL-1620 version bump and release notes
[jalview.git] / src / jalview / gui / Jalview2XML.java
index 39bd626..f3854fe 100644 (file)
 /*
- * 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 2.8.2b1)
+ * Copyright (C) 2014 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.
- * 
+ * 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 uk.ac.vamsas.objects.utils.MapList;
+import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
-import jalview.schemabinding.version2.*;
-import jalview.schemes.*;
+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.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.jarInputStreamProvider;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+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.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+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.
@@ -273,6 +343,8 @@ public class Jalview2XML
       return;
     }
 
+    Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
+
     try
     {
 
@@ -327,6 +399,7 @@ public class Jalview2XML
           }
 
           int ap, apSize = af.alignPanels.size();
+
           for (ap = 0; ap < apSize; ap++)
           {
             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
@@ -338,9 +411,21 @@ public class Jalview2XML
             }
 
             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();
@@ -370,6 +455,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
@@ -380,8 +466,14 @@ public class Jalview2XML
           jfileName = jfileName + ".xml";
         }
         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();
@@ -399,6 +491,22 @@ 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
    * JarOutputStream
@@ -415,6 +523,28 @@ public class Jalview2XML
   public JalviewModel SaveState(AlignmentPanel ap, String fileName,
           JarOutputStream jout)
   {
+    return SaveState(ap, fileName, false, jout);
+  }
+
+  /**
+   * create a JalviewModel from an algnment 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();
@@ -425,7 +555,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();
 
@@ -444,6 +575,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)
     {
@@ -459,13 +595,16 @@ public class Jalview2XML
     }
 
     JSeq jseq;
+    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)
@@ -497,28 +636,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)
@@ -560,9 +705,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();
@@ -571,7 +716,10 @@ public class Jalview2XML
 
           pdb.setId(entry.getId());
           pdb.setType(entry.getType());
-
+          //
+          // store any JMol views associated with this seqeunce
+          // this section copes with duplicate entries in the project, so a
+          // dataset only view *should* be coped with sensibly
           AppJmol jmol;
           // This must have been loaded, is it still visible?
           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
@@ -590,7 +738,9 @@ public class Jalview2XML
                                 .startsWith(
                                         jmol.jmb.pdbentry[peid].getId()
                                                 .toLowerCase())))
+                {
                   continue;
+                }
                 if (matchedFile == null)
                 {
                   matchedFile = jmol.jmb.pdbentry[peid].getFile();
@@ -611,8 +761,8 @@ public class Jalview2XML
 
                 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])
+                  // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+                  if (jds == jmol.jmb.sequence[peid][smap])
                   {
                     StructureState state = new StructureState();
                     state.setVisible(true);
@@ -638,6 +788,7 @@ public class Jalview2XML
                     }
                     pdb.addStructureState(state);
                   }
+
                 }
               }
             }
@@ -706,7 +857,7 @@ public class Jalview2XML
       jms.addJSeq(jseq);
     }
 
-    if (av.hasHiddenRows())
+    if (!storeDS && av.hasHiddenRows())
     {
       jal = av.getAlignment();
     }
@@ -750,7 +901,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
@@ -792,142 +943,33 @@ public class Jalview2XML
         }
       }
     }
-
     // SAVE ANNOTATIONS
     /**
      * 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)
+        // Store annotation on dataset sequences only
+        jalview.datamodel.AlignmentAnnotation[] aa = sq.getAnnotation();
+        if (aa != null && aa.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)
-          {
-            // 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);
-          }
+          storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+                  vamsasSet);
         }
-        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)
-        {
-          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
@@ -935,7 +977,7 @@ public class Jalview2XML
     {
       JGroup[] groups = new JGroup[jal.getGroups().size()];
       int i = -1;
-      for (jalview.datamodel.SequenceGroup sg:jal.getGroups())
+      for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
       {
         groups[++i] = new JGroup();
 
@@ -966,10 +1008,10 @@ 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)
           {
@@ -995,6 +1037,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
@@ -1005,260 +1048,540 @@ 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.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          view.setConsThreshold(cs.getConservationInc());
+          if (cs instanceof jalview.schemes.UserColourScheme)
+          {
+            view.setBgColour(SetUserColourScheme(cs, userColours, jms));
+          }
         }
-      }
 
-      if (cs instanceof ResidueColourScheme)
-      {
-        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();
+      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.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.featuresDisplayed != null)
+      {
+        jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-      String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
+        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++)
+        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)
-          {
-            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());
+          for (int ro = 0; ro < renderOrder.length; ro++)
+          {
+            gstyle = ap.seqPanel.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.seqPanel.seqCanvas.getFeatureRenderer()
+                      .getColour(renderOrder[ro]).getRGB());
+            }
+
+            setting.setDisplay(av.featuresDisplayed
+                    .containsKey(renderOrder[ro]));
+            float rorder = ap.seqPanel.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.seqPanel.seqCanvas.getFeatureRenderer().featureColours
+                .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]));
+          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(renderOrder[ro]);
+                  .getOrder(key);
           if (rorder > -1)
           {
             setting.setOrder(rorder);
           }
           fs.addSetting(setting);
-          settingsAdded.addElement(renderOrder[ro]);
+          settingsAdded.addElement(key);
         }
+        en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
+                .keySet().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.seqPanel.seqCanvas
+                  .getFeatureRenderer().featureGroups.get(grp))
+                  .booleanValue());
+          fs.addGroup(g);
+          groupsAdded.addElement(grp);
+        }
+        jms.setFeatureSettings(fs);
+
       }
 
-      // Make sure we save none displayed feature settings
-      Enumeration en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
-              .keys();
-      while (en.hasMoreElements())
+      if (av.hasHiddenColumns())
       {
-        String key = en.nextElement().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.");
+        }
+        else
+        {
+          for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
+                  .size(); c++)
+          {
+            int[] region = (int[]) av.getColumnSelection()
+                    .getHiddenColumns().elementAt(c);
+            HiddenColumns hc = new HiddenColumns();
+            hc.setStart(region[0]);
+            hc.setEnd(region[1]);
+            view.addHiddenColumns(hc);
+          }
+        }
+      }
+      if (calcIdSet.size() > 0)
+      {
+        for (String calcId : calcIdSet)
+        {
+          if (calcId.trim().length() > 0)
+          {
+            CalcIdParam cidp = createCalcIdParam(calcId, av);
+            // Some calcIds have no parameters.
+            if (cidp != null)
+            {
+              view.addCalcIdParam(cidp);
+            }
+          }
         }
+      }
+
+      jms.addViewport(view);
+    }
+    object.setJalviewModelSequence(jms);
+    object.getVamsasModel().addSequenceSet(vamsasSet);
 
-        Setting setting = new Setting();
-        setting.setType(key);
-        setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
-                .getColour(key).getRGB());
+    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
+      {
+        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)
+      {
+        // TODO: raise error in GUI if marshalling failed.
+        ex.printStackTrace();
+      }
+    }
+    return object;
+  }
+
+  private AnnotationColours constructAnnotationColours(
+          AnnotationColourGradient acg, Vector 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;
+  }
 
-        setting.setDisplay(false);
-        float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
-                key);
-        if (rorder > -1)
+  private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
+          IdentityHashMap groupRefs, AlignmentViewport av,
+          Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
+  {
+
+    for (int i = 0; i < aa.length; i++)
+    {
+      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)
+      {
+        // 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)
         {
-          setting.setOrder(rorder);
+          // make a locally unique String
+          groupRefs.put(aa[i].groupRef,
+                  groupIdr = ("" + System.currentTimeMillis()
+                          + aa[i].groupRef.getName() + groupRefs.size()));
         }
-        fs.addSetting(setting);
-        settingsAdded.addElement(key);
+        an.setGroupRef(groupIdr.toString());
       }
-      en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups.keys();
-      Vector groupsAdded = new Vector();
-      while (en.hasMoreElements())
+
+      // 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)
       {
-        String grp = en.nextElement().toString();
-        if (groupsAdded.contains(grp))
+        an.setGraph(true);
+        an.setGraphType(aa[i].graph);
+        an.setGraphGroup(aa[i].graphGroup);
+        if (aa[i].getThreshold() != null)
         {
-          continue;
+          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);
         }
-        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);
+      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)
+          {
+            // 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);
+      }
+      if (!storeDS || (storeDS && !aa[i].autoCalculated))
+      {
+        // skip autocalculated annotation - these are only provided for
+        // alignments
+        vamsasSet.addAnnotation(an);
+      }
     }
 
-    if (av.hasHiddenColumns())
-    {
-      if (av.getColumnSelection() == null
-              || av.getColumnSelection().getHiddenColumns() == null)
+  }
+
+  private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
+  {
+    AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
+    if (settings != null)
+    {
+      CalcIdParam vCalcIdParam = new CalcIdParam();
+      vCalcIdParam.setCalcId(calcId);
+      vCalcIdParam.addServiceURL(settings.getServiceURI());
+      // generic URI allowing a third party to resolve another instance of the
+      // service used for this calculation
+      for (String urls : settings.getServiceURLs())
       {
-        warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+        vCalcIdParam.addServiceURL(urls);
+      }
+      vCalcIdParam.setVersion("1.0");
+      if (settings.getPreset() != null)
+      {
+        WsParamSetI setting = settings.getPreset();
+        vCalcIdParam.setName(setting.getName());
+        vCalcIdParam.setDescription(setting.getDescription());
       }
       else
       {
-        for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
-                .size(); c++)
+        vCalcIdParam.setName("");
+        vCalcIdParam.setDescription("Last used parameters");
+      }
+      // need to be able to recover 1) settings 2) user-defined presets or
+      // recreate settings from preset 3) predefined settings provided by
+      // service - or settings that can be transferred (or discarded)
+      vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
+              "|\\n|"));
+      vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
+      // todo - decide if updateImmediately is needed for any projects.
+
+      return vCalcIdParam;
+    }
+    return null;
+  }
+
+  private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
+          AlignViewport av)
+  {
+    if (calcIdParam.getVersion().equals("1.0"))
+    {
+      Jws2Instance service = Jws2Discoverer.getDiscoverer()
+              .getPreferredServiceFor(calcIdParam.getServiceURL());
+      if (service != null)
+      {
+        WsParamSetI parmSet = null;
+        try
+        {
+          parmSet = service.getParamStore().parseServiceParameterFile(
+                  calcIdParam.getName(), calcIdParam.getDescription(),
+                  calcIdParam.getServiceURL(),
+                  calcIdParam.getParameters().replace("|\\n|", "\n"));
+        } catch (IOException x)
+        {
+          warn("Couldn't parse parameter data for "
+                  + calcIdParam.getCalcId(), x);
+          return false;
+        }
+        List<ArgumentI> argList = null;
+        if (calcIdParam.getName().length() > 0)
+        {
+          parmSet = service.getParamStore()
+                  .getPreset(calcIdParam.getName());
+          if (parmSet != null)
+          {
+            // TODO : check we have a good match with settings in AACon -
+            // otherwise we'll need to create a new preset
+          }
+        }
+        else
         {
-          int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
-                  .elementAt(c);
-          HiddenColumns hc = new HiddenColumns();
-          hc.setStart(region[0]);
-          hc.setEnd(region[1]);
-          view.addHiddenColumns(hc);
+          argList = parmSet.getArguments();
+          parmSet = null;
         }
+        AAConSettings settings = new AAConSettings(
+                calcIdParam.isAutoUpdate(), service, parmSet, argList);
+        av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
+                calcIdParam.isNeedsUpdate());
+        return true;
       }
-    }
-
-    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
-      {
-        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)
+      else
       {
-        // TODO: raise error in GUI if marshalling failed.
-        ex.printStackTrace();
+        warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
+        return false;
       }
     }
-    return object;
+    throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
   }
 
   /**
@@ -1550,6 +1873,8 @@ public class Jalview2XML
 
     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
@@ -1557,10 +1882,26 @@ public class Jalview2XML
 
       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
       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;
   }
@@ -1583,6 +1924,7 @@ public class Jalview2XML
     return new jarInputStreamProvider()
     {
 
+      @Override
       public JarInputStream getJarInputStream() throws IOException
       {
         if (_url != null)
@@ -1595,6 +1937,7 @@ public class Jalview2XML
         }
       }
 
+      @Override
       public String getFilename()
       {
         return file;
@@ -1631,7 +1974,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
@@ -1658,10 +2001,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++;
@@ -1759,6 +2106,7 @@ public class Jalview2XML
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
             JOptionPane.showInternalMessageDialog(Desktop.desktop,
@@ -1776,22 +2124,26 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Hashtable alreadyLoadedPDB;
+  Hashtable<String, String> alreadyLoadedPDB;
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
    * sync if this is set to true.
    */
-  private boolean updateLocalViews = false;
+  private final boolean updateLocalViews = false;
 
   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
   {
     if (alreadyLoadedPDB == null)
+    {
       alreadyLoadedPDB = new Hashtable();
+    }
 
     if (alreadyLoadedPDB.containsKey(pdbId))
+    {
       return alreadyLoadedPDB.get(pdbId).toString();
+    }
 
     try
     {
@@ -1827,7 +2179,7 @@ public class Jalview2XML
         }
         ;
         out.close();
-        String t=outFile.getAbsolutePath();
+        String t = outFile.getAbsolutePath();
         alreadyLoadedPDB.put(pdbId, t);
         return t;
       }
@@ -1883,7 +2235,9 @@ public class Jalview2XML
 
     JalviewModelSequence jms = object.getJalviewModelSequence();
 
-    Viewport view = jms.getViewport(0);
+    Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
+            : null;
+
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -1902,7 +2256,7 @@ public class Jalview2XML
 
       if (seqRefIds.get(seqId) != null)
       {
-        tmpseqs.add((jalview.datamodel.Sequence) seqRefIds.get(seqId));
+        tmpseqs.add(seqRefIds.get(seqId));
         multipleView = true;
       }
       else
@@ -1925,8 +2279,7 @@ public class Jalview2XML
           hiddenSeqs = new Vector();
         }
 
-        hiddenSeqs.addElement((jalview.datamodel.Sequence) seqRefIds
-                .get(seqId));
+        hiddenSeqs.addElement(seqRefIds.get(seqId));
       }
 
     }
@@ -1960,7 +2313,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);
     }
     // ///////////////////////////////
 
@@ -2022,7 +2378,9 @@ public class Jalview2XML
                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
               }
             }
-
+            StructureSelectionManager.getStructureSelectionManager(
+                    Desktop.instance)
+                    .registerPDBEntry(entry);
             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
           }
         }
@@ -2138,7 +2496,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);
 
@@ -2147,16 +2507,21 @@ public class Jalview2XML
         // Construct new annotation from model.
         AnnotationElement[] ae = an[i].getAnnotationElement();
         jalview.datamodel.Annotation[] anot = null;
-
+        java.awt.Color firstColour = null;
+        int anpos;
         if (!an[i].getScoreOnly())
         {
           anot = new jalview.datamodel.Annotation[al.getWidth()];
           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
           {
-            if (ae[aa].getPosition() >= anot.length)
+            anpos = ae[aa].getPosition();
+
+            if (anpos >= anot.length)
+            {
               continue;
+            }
 
-            anot[ae[aa].getPosition()] = new jalview.datamodel.Annotation(
+            anot[anpos] = new jalview.datamodel.Annotation(
 
             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
                     (ae[aa].getSecondaryStructure() == null || ae[aa]
@@ -2172,8 +2537,11 @@ public class Jalview2XML
             // {
             // anot[ae[aa].getPosition()].displayCharacter = "";
             // }
-            anot[ae[aa].getPosition()].colour = new java.awt.Color(
-                    ae[aa].getColour());
+            anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
+            if (firstColour == null)
+            {
+              firstColour = anot[anpos].colour;
+            }
           }
         }
         jalview.datamodel.AlignmentAnnotation jaa = null;
@@ -2189,7 +2557,7 @@ public class Jalview2XML
                   an[i].getGraphType());
 
           jaa.graphGroup = an[i].getGraphGroup();
-
+          jaa._linecolour = firstColour;
           if (an[i].getThresholdLine() != null)
           {
             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
@@ -2209,25 +2577,22 @@ public class Jalview2XML
         {
           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
                   an[i].getDescription(), anot);
+          jaa._linecolour = firstColour;
         }
-        if (autoForView)
+        // register new annotation
+        if (an[i].getId() != null)
         {
-          // register new annotation
-          if (an[i].getId() != null)
-          {
-            annotationIds.put(an[i].getId(), jaa);
-            jaa.annotationId = an[i].getId();
-          }
-          // recover sequence association
-          if (an[i].getSequenceRef() != null)
+          annotationIds.put(an[i].getId(), jaa);
+          jaa.annotationId = an[i].getId();
+        }
+        // recover sequence association
+        if (an[i].getSequenceRef() != null)
+        {
+          if (al.findName(an[i].getSequenceRef()) != null)
           {
-            if (al.findName(an[i].getSequenceRef()) != null)
-            {
-              jaa.createSequenceMapping(
-                      al.findName(an[i].getSequenceRef()), 1, true);
-              al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(
-                      jaa);
-            }
+            jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
+                    1, true);
+            al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
           }
         }
         // and make a note of any group association
@@ -2248,10 +2613,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())
         {
@@ -2270,10 +2639,17 @@ public class Jalview2XML
         }
         if (an[i].hasBelowAlignment())
         {
-          jaa.belowAlignment=an[i].isBelowAlignment();
+          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));
@@ -2287,14 +2663,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;
@@ -2305,6 +2680,12 @@ public class Jalview2XML
           {
             cs = GetUserColourScheme(jms, groups[i].getColour());
           }
+          else if (groups[i].getColour().equals("AnnotationColourGradient")
+                  && groups[i].getAnnotationColours() != null)
+          {
+            addAnnotSchemeGroup = true;
+            cs = null;
+          }
           else
           {
             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
@@ -2356,6 +2737,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());
@@ -2399,10 +2784,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
 
@@ -2446,6 +2840,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)
@@ -2614,7 +3015,7 @@ public class Jalview2XML
               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
               // seqs_file 2}, boolean[] {
               // linkAlignPanel,superposeWithAlignpanel}} from hash
-              Object[] jmoldat = (Object[]) jmolViewIds.get(sviewid);
+              Object[] jmoldat = jmolViewIds.get(sviewid);
               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
                       s).getAlignwithAlignPanel() : false;
@@ -2637,13 +3038,12 @@ public class Jalview2XML
               }
               if (ids[p].getFile() != null)
               {
-                File mapkey=new File(ids[p].getFile());
+                File mapkey = new File(ids[p].getFile());
                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
                         .get(mapkey);
                 if (seqstrmaps == null)
                 {
-                  ((Hashtable) jmoldat[2]).put(
-                          mapkey,
+                  ((Hashtable) jmoldat[2]).put(mapkey,
                           seqstrmaps = new Object[]
                           { pdbFile, ids[p].getId(), new Vector(),
                               new Vector() });
@@ -2740,25 +3140,28 @@ public class Jalview2XML
               {
                 newFileLoc = new StringBuffer();
               }
-              do {
+              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((SequenceI[]) ((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);
+                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)
             {
@@ -2780,7 +3183,7 @@ public class Jalview2XML
                 newFileLoc.append(((String) filedat[0]));
                 pdbfilenames.addElement((String) filedat[0]);
                 pdbids.addElement((String) filedat[1]);
-                seqmaps.addElement((SequenceI[]) ((Vector<SequenceI>) filedat[2])
+                seqmaps.addElement(((Vector<SequenceI>) filedat[2])
                         .toArray(new SequenceI[0]));
                 newFileLoc.append(" \"");
                 newFileLoc.append((String) filedat[0]);
@@ -2816,10 +3219,10 @@ public class Jalview2XML
               // TODO: assemble String[] { pdb files }, String[] { id for each
               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
               // seqs_file 2}} from hash
-              final String[] pdbf = (String[]) pdbfilenames
-                      .toArray(new String[pdbfilenames.size()]), id = (String[]) pdbids
+              final String[] pdbf = pdbfilenames
+                      .toArray(new String[pdbfilenames.size()]), id = pdbids
                       .toArray(new String[pdbids.size()]);
-              final SequenceI[][] sq = (SequenceI[][]) seqmaps
+              final SequenceI[][] sq = seqmaps
                       .toArray(new SequenceI[seqmaps.size()][]);
               final String fileloc = newFileLoc.toString(), vid = sviewid;
               final AlignFrame alf = af;
@@ -2829,14 +3232,17 @@ public class Jalview2XML
               {
                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
                 {
+                  @Override
                   public void run()
                   {
-                    AppJmol sview = null;
+                    JalviewStructureDisplayI sview = null;
                     try
                     {
-                      sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
+                      // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
+                      sview = new StructureViewer(alf.alignPanel.getStructureSelectionManager()).createView(StructureViewer.Viewer.JMOL, pdbf, id, sq, alf.alignPanel,
                               useinJmolsuperpos, usetoColourbyseq,
                               jmolColouring, fileloc, rect, vid);
+                      addNewStructureViewer(sview);
                     } catch (OutOfMemoryError ex)
                     {
                       new OOMWarning("restoring structure view for PDB id "
@@ -2875,30 +3281,29 @@ public class Jalview2XML
               // viewer
               Object[] filedat = oldFiles.get(id);
               String pdbFile = (String) filedat[0];
-              SequenceI[] seq = (SequenceI[]) ((Vector<SequenceI>) filedat[2])
+              SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
                       .toArray(new SequenceI[0]);
-              ((AppJmol) comp).jmb.ssm.setMapping(seq, null, pdbFile,
+              comp.jmb.ssm.setMapping(seq, null, pdbFile,
                       jalview.io.AppletFormatAdapter.FILE);
-              ((AppJmol) comp).jmb.addSequenceForStructFile(pdbFile, seq);
+              comp.jmb.addSequenceForStructFile(pdbFile, seq);
             }
             // and add the AlignmentPanel's reference to the Jmol view
-            ((AppJmol) comp).addAlignmentPanel(ap);
+            comp.addAlignmentPanel(ap);
             if (useinJmolsuperpos)
             {
-              ((AppJmol) comp).useAlignmentPanelForSuperposition(ap);
+              comp.useAlignmentPanelForSuperposition(ap);
             }
             else
             {
-              ((AppJmol) comp).excludeAlignmentPanelForSuperposition(ap);
+              comp.excludeAlignmentPanelForSuperposition(ap);
             }
             if (usetoColourbyseq)
             {
-              ((AppJmol) comp).useAlignmentPanelForColourbyseq(ap,
-                      !jmolColouring);
+              comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
             }
             else
             {
-              ((AppJmol) comp).excludeAlignmentPanelForColourbyseq(ap);
+              comp.excludeAlignmentPanelForColourbyseq(ap);
             }
           }
         }
@@ -2908,6 +3313,86 @@ public class Jalview2XML
     return af;
   }
 
+  /**
+   * 
+   * @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"))
+    {
+      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())
+      {
+        // 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
+        {
+          if (Float.valueOf(curT) > Float.valueOf(fileT))
+          {
+            // 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;
+  }
+
+  Vector<JalviewStructureDisplayI> newStructureViewers = null;
+
+  protected void addNewStructureViewer(JalviewStructureDisplayI sview)
+  {
+    if (newStructureViewers != null)
+    {
+      sview.getBinding().setFinishedLoadingFromArchive(false);
+      newStructureViewers.add(sview);
+    }
+  }
+
+  protected void setLoadingFinishedForNewStructureViewers()
+  {
+    if (newStructureViewers != null)
+    {
+      for (JalviewStructureDisplayI sview : newStructureViewers)
+      {
+        sview.getBinding().setFinishedLoadingFromArchive(true);
+      }
+      newStructureViewers.clear();
+      newStructureViewers = null;
+    }
+  }
+
   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
           Alignment al, JalviewModelSequence jms, Viewport view,
           String uniqueSeqSetId, String viewId,
@@ -2921,8 +3406,8 @@ public class Jalview2XML
 
     for (int i = 0; i < JSEQ.length; i++)
     {
-      af.viewport.setSequenceColour(af.viewport.getAlignment().getSequenceAt(i),
-              new java.awt.Color(JSEQ[i].getColour()));
+      af.viewport.setSequenceColour(af.viewport.getAlignment()
+              .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
     af.viewport.gatherViewsHere = view.getGatheredViews();
@@ -3021,93 +3506,11 @@ public class Jalview2XML
       }
       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 = (jalview.datamodel.SequenceGroup) al
-                          .getGroups().elementAt(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
       {
@@ -3143,8 +3546,8 @@ public class Jalview2XML
     }
     if (view.hasIgnoreGapsinConsensus())
     {
-      af.viewport.setIgnoreGapsConsensus(view
-              .getIgnoreGapsinConsensus(), null);
+      af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
+              null);
     }
     if (view.hasFollowHighlight())
     {
@@ -3171,6 +3574,10 @@ public class Jalview2XML
     {
       af.viewport.setShowSequenceLogo(false);
     }
+    if (view.hasNormaliseSequenceLogo())
+    {
+      af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
+    }
     if (view.hasShowDbRefTooltip())
     {
       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
@@ -3239,12 +3646,16 @@ public class Jalview2XML
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
+        {
           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
                   setting.getType(), setting.getOrder());
+        }
         else
+        {
           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
                   setting.getType(),
                   fs / jms.getFeatureSettings().getSettingCount());
+        }
         if (setting.getDisplay())
         {
           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
@@ -3270,16 +3681,158 @@ public class Jalview2XML
                 );
       }
     }
-
+    if (view.getCalcIdParam() != null)
+    {
+      for (CalcIdParam calcIdParam : view.getCalcIdParam())
+      {
+        if (calcIdParam != null)
+        {
+          if (recoverCalcIdParam(calcIdParam, af.viewport))
+          {
+          }
+          else
+          {
+            warn("Couldn't recover parameters for "
+                    + calcIdParam.getCalcId());
+          }
+        }
+      }
+    }
     af.setMenusFromViewport(af.viewport);
     // 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)
   {
@@ -3300,21 +3853,34 @@ public class Jalview2XML
       }
       for (JvAnnotRow auan : autoAlan)
       {
-        visan.put(auan.template.label, auan);
+        visan.put(auan.template.label
+                + (auan.template.getCalcId() == null ? "" : "\t"
+                        + auan.template.getCalcId()), auan);
       }
       int hSize = al.getAlignmentAnnotation().length;
       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      // work through any autoCalculated annotation already on the view
+      // removing it if it should be placed in a different location on the
+      // annotation panel.
+      List<String> remains = new ArrayList(visan.keySet());
       for (int h = 0; h < hSize; h++)
       {
         jalview.datamodel.AlignmentAnnotation jalan = al
                 .getAlignmentAnnotation()[h];
         if (jalan.autoCalculated)
         {
-          JvAnnotRow valan = visan.get(jalan.label);
+          String k;
+          JvAnnotRow valan = visan.get(k = jalan.label);
+          if (jalan.getCalcId() != null)
+          {
+            valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
+          }
+
           if (valan != null)
           {
             // delete the auto calculated row from the alignment
-            al.deleteAnnotation(al.getAlignmentAnnotation()[h], false);
+            al.deleteAnnotation(jalan, false);
+            remains.remove(k);
             hSize--;
             h--;
             if (valan != nullAnnot)
@@ -3336,6 +3902,18 @@ public class Jalview2XML
           }
         }
       }
+      // Add any (possibly stale) autocalculated rows that were not appended to
+      // the view during construction
+      for (String other : remains)
+      {
+        JvAnnotRow othera = visan.get(other);
+        if (othera != nullAnnot && othera.template.getCalcId() != null
+                && othera.template.getCalcId().length() > 0)
+        {
+          reorder.add(othera);
+        }
+      }
+      // now put the automatic annotation in its correct place
       int s = 0, srt[] = new int[reorder.size()];
       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
       for (JvAnnotRow jvar : reorder)
@@ -3410,7 +3988,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;
@@ -3422,7 +4001,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)
@@ -3435,7 +4014,7 @@ 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);
     }
@@ -3451,7 +4030,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
@@ -3460,9 +4039,12 @@ public class Jalview2XML
     jalview.datamodel.SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
-      dsq = (jalview.datamodel.SequenceI) sq.getDatasetSequence();
+      dsq = sq.getDatasetSequence();
+    }
+    if (sq == null && ignoreUnrefed)
+    {
+      return;
     }
-
     String sqid = vamsasSeq.getDsseqid();
     if (dsq == null)
     {
@@ -3500,6 +4082,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);
+              }
+            }
+          }
         }
       }
     }
@@ -3526,11 +4126,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" : ""));
       }
@@ -3747,7 +4348,7 @@ public class Jalview2XML
    * flag indicating if hashtables should be cleared on finalization TODO this
    * flag may not be necessary
    */
-  private boolean _cleartables = true;
+  private final boolean _cleartables = true;
 
   private Hashtable jvids2vobj;
 
@@ -3756,6 +4357,7 @@ public class Jalview2XML
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     // really make sure we have no buried refs left.
@@ -3900,7 +4502,9 @@ public class Jalview2XML
         }
       }
       else
+      {
         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
+      }
     }
   }