JAL-3116 parse EMBL XML with JAXB (todo: update unit tests)
[jalview.git] / src / jalview / gui / Jalview2XML.java
index c0e1ebe..87b8d87 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.analysis.Conservation;
 import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.api.structures.JalviewStructureDisplayI;
@@ -28,20 +29,31 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.GraphLine;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.RnaViewerModel;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
 import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.ext.varna.RnaModel;
 import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.renderer.ResidueShaderI;
 import jalview.schemabinding.version2.AlcodMap;
 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.Colour;
+import jalview.schemabinding.version2.CompoundMatcher;
 import jalview.schemabinding.version2.DBRef;
 import jalview.schemabinding.version2.Features;
 import jalview.schemabinding.version2.Group;
@@ -54,6 +66,8 @@ import jalview.schemabinding.version2.MapListFrom;
 import jalview.schemabinding.version2.MapListTo;
 import jalview.schemabinding.version2.Mapping;
 import jalview.schemabinding.version2.MappingChoice;
+import jalview.schemabinding.version2.MatchCondition;
+import jalview.schemabinding.version2.MatcherSet;
 import jalview.schemabinding.version2.OtherData;
 import jalview.schemabinding.version2.PdbentryItem;
 import jalview.schemabinding.version2.Pdbids;
@@ -69,20 +83,25 @@ import jalview.schemabinding.version2.ThresholdLine;
 import jalview.schemabinding.version2.Tree;
 import jalview.schemabinding.version2.UserColours;
 import jalview.schemabinding.version2.Viewport;
+import jalview.schemabinding.version2.types.ColourThreshTypeType;
+import jalview.schemabinding.version2.types.FeatureMatcherByType;
+import jalview.schemabinding.version2.types.NoValueColour;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.FeatureColour;
-import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.util.jarInputStreamProvider;
+import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -108,6 +127,8 @@ import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -125,7 +146,6 @@ 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.Marshaller;
@@ -165,6 +185,8 @@ public class Jalview2XML
    */
   Map<String, SequenceI> seqRefIds = null;
 
+  Map<String, SequenceI> incompleteSeqs = null;
+
   List<SeqFref> frefedSequence = null;
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
@@ -173,13 +195,13 @@ public class Jalview2XML
    * Map of reconstructed AlignFrame objects that appear to have come from
    * SplitFrame objects (have a dna/protein complement view).
    */
-  private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<Viewport, AlignFrame>();
+  private Map<Viewport, AlignFrame> splitFrameCandidates = new HashMap<>();
 
   /*
    * Map from displayed rna structure models to their saved session state jar
    * entry names
    */
-  private Map<RnaModel, String> rnaSessions = new HashMap<RnaModel, String>();
+  private Map<RnaModel, String> rnaSessions = new HashMap<>();
 
   /**
    * create/return unique hash string for sq
@@ -208,43 +230,23 @@ public class Jalview2XML
     }
   }
 
-  void clearSeqRefs()
-  {
-    if (_cleartables)
-    {
-      if (seqRefIds != null)
-      {
-        seqRefIds.clear();
-      }
-      if (seqsToIds != null)
-      {
-        seqsToIds.clear();
-      }
-      // seqRefIds = null;
-      // seqsToIds = null;
-    }
-    else
-    {
-      // do nothing
-      warn("clearSeqRefs called when _cleartables was not set. Doing nothing.");
-      // seqRefIds = new Hashtable();
-      // seqsToIds = new IdentityHashMap();
-    }
-  }
-
   void initSeqRefs()
   {
     if (seqsToIds == null)
     {
-      seqsToIds = new IdentityHashMap<SequenceI, String>();
+      seqsToIds = new IdentityHashMap<>();
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new HashMap<String, SequenceI>();
+      seqRefIds = new HashMap<>();
+    }
+    if (incompleteSeqs == null)
+    {
+      incompleteSeqs = new HashMap<>();
     }
     if (frefedSequence == null)
     {
-      frefedSequence = new ArrayList<SeqFref>();
+      frefedSequence = new ArrayList<>();
     }
   }
 
@@ -267,9 +269,12 @@ public class Jalview2XML
   {
     String sref;
 
-    public SeqFref(String _sref)
+    String type;
+
+    public SeqFref(String _sref, String type)
     {
       sref = _sref;
+      this.type = type;
     }
 
     public String getSref()
@@ -299,10 +304,17 @@ public class Jalview2XML
       }
       return sq;
     }
+
     /**
      * @return true if the forward reference was fully resolved
      */
     abstract boolean resolve();
+
+    @Override
+    public String toString()
+    {
+      return type + " reference to " + sref;
+    }
   }
 
   /**
@@ -315,7 +327,7 @@ public class Jalview2XML
   public SeqFref newMappingRef(final String sref,
           final jalview.datamodel.Mapping _jmap)
   {
-    SeqFref fref = new SeqFref(sref)
+    SeqFref fref = new SeqFref(sref, "Mapping")
     {
       public jalview.datamodel.Mapping jmap = _jmap;
 
@@ -335,16 +347,23 @@ public class Jalview2XML
   }
 
   public SeqFref newAlcodMapRef(final String sref,
-          final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap)
+          final AlignedCodonFrame _cf,
+          final jalview.datamodel.Mapping _jmap)
   {
 
-    SeqFref fref = new SeqFref(sref)
+    SeqFref fref = new SeqFref(sref, "Codon Frame")
     {
       AlignedCodonFrame cf = _cf;
 
       public jalview.datamodel.Mapping mp = _jmap;
 
       @Override
+      public boolean isResolvable()
+      {
+        return super.isResolvable() && mp.getTo() != null;
+      };
+
+      @Override
       boolean resolve()
       {
         SequenceI seq = getSrefDatasetSeq();
@@ -361,47 +380,75 @@ public class Jalview2XML
 
   public void resolveFrefedSequences()
   {
-    Iterator<SeqFref> nextFref=frefedSequence.iterator();
-    int toresolve=frefedSequence.size();
-    int unresolved=0,failedtoresolve=0;
-    while (nextFref.hasNext()) {
+    Iterator<SeqFref> nextFref = frefedSequence.iterator();
+    int toresolve = frefedSequence.size();
+    int unresolved = 0, failedtoresolve = 0;
+    while (nextFref.hasNext())
+    {
       SeqFref ref = nextFref.next();
       if (ref.isResolvable())
       {
-        try {
+        try
+        {
           if (ref.resolve())
           {
             nextFref.remove();
-          } else {
+          }
+          else
+          {
             failedtoresolve++;
           }
-        } catch (Exception x) {
-          System.err.println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "+ref.getSref());
+        } catch (Exception x)
+        {
+          System.err.println(
+                  "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
+                          + ref.getSref());
           x.printStackTrace();
           failedtoresolve++;
-        } 
-      } else {
+        }
+      }
+      else
+      {
         unresolved++;
       }
     }
-    if (unresolved>0)
+    if (unresolved > 0)
     {
-      System.err.println("There were "+unresolved+" forward references left unresolved on the stack.");
+      System.err.println("Jalview Project Import: There were " + unresolved
+              + " forward references left unresolved on the stack.");
     }
-    if (failedtoresolve>0)
+    if (failedtoresolve > 0)
     {
       System.err.println("SERIOUS! " + failedtoresolve
               + " resolvable forward references failed to resolve.");
     }
+    if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+    {
+      System.err.println(
+              "Jalview Project Import: There are " + incompleteSeqs.size()
+                      + " sequences which may have incomplete metadata.");
+      if (incompleteSeqs.size() < 10)
+      {
+        for (SequenceI s : incompleteSeqs.values())
+        {
+          System.err.println(s.toString());
+        }
+      }
+      else
+      {
+        System.err.println(
+                "Too many to report. Skipping output of incomplete sequences.");
+      }
+    }
   }
 
   /**
    * This maintains a map of viewports, the key being the seqSetId. Important to
    * set historyItem and redoList for multiple views
    */
-  Map<String, AlignViewport> viewportsAdded = new HashMap<String, AlignViewport>();
+  Map<String, AlignViewport> viewportsAdded = new HashMap<>();
 
-  Map<String, AlignmentAnnotation> annotationIds = new HashMap<String, AlignmentAnnotation>();
+  Map<String, AlignmentAnnotation> annotationIds = new HashMap<>();
 
   String uniqueSetSuffix = "";
 
@@ -463,8 +510,21 @@ public class Jalview2XML
     {
       return;
     }
+    saveAllFrames(Arrays.asList(frames), jout);
+  }
 
-    Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
+  /**
+   * core method for storing state for a set of AlignFrames.
+   * 
+   * @param frames
+   *          - frames involving all data to be exported (including containing
+   *          splitframes)
+   * @param jout
+   *          - project output stream
+   */
+  private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
+  {
+    Hashtable<String, AlignFrame> dsses = new Hashtable<>();
 
     /*
      * ensure cached data is clear before starting
@@ -479,17 +539,16 @@ public class Jalview2XML
       // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
       // //////////////////////////////////////////////////
 
-      List<String> shortNames = new ArrayList<String>();
-      List<String> viewIds = new ArrayList<String>();
+      List<String> shortNames = new ArrayList<>();
+      List<String> viewIds = new ArrayList<>();
 
       // REVERSE ORDER
-      for (int i = frames.length - 1; i > -1; i--)
+      for (int i = frames.size() - 1; i > -1; i--)
       {
-        AlignFrame af = frames[i];
+        AlignFrame af = frames.get(i);
         // skip ?
-        if (skipList != null
-                && skipList
-                        .containsKey(af.getViewport().getSequenceSetId()))
+        if (skipList != null && skipList
+                .containsKey(af.getViewport().getSequenceSetId()))
         {
           continue;
         }
@@ -509,8 +568,8 @@ public class Jalview2XML
 
           saveState(apanel, fileName, jout, viewIds);
 
-          String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                  .getDataset());
+          String dssid = getDatasetIdRef(
+                  af.getViewport().getAlignment().getDataset());
           if (!dsses.containsKey(dssid))
           {
             dsses.put(dssid, af);
@@ -556,8 +615,8 @@ public class Jalview2XML
 
     if (shortName.indexOf(File.separatorChar) > -1)
     {
-      shortName = shortName.substring(shortName
-              .lastIndexOf(File.separatorChar) + 1);
+      shortName = shortName
+              .substring(shortName.lastIndexOf(File.separatorChar) + 1);
     }
 
     int count = 1;
@@ -588,30 +647,20 @@ public class Jalview2XML
   {
     try
     {
-      int ap = 0;
-      int apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
-      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-      List<String> viewIds = new ArrayList<String>();
+      List<AlignFrame> frames = new ArrayList<>();
 
-      for (AlignmentPanel apanel : af.alignPanels)
+      // resolve splitframes
+      if (af.getViewport().getCodingComplement() != null)
       {
-        String jfileName = apSize == 1 ? fileName : fileName + ap;
-        ap++;
-        if (!jfileName.endsWith(".xml"))
-        {
-          jfileName = jfileName + ".xml";
-        }
-        saveState(apanel, jfileName, jout, viewIds);
-        String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                .getDataset());
-        if (!dsses.containsKey(dssid))
-        {
-          dsses.put(dssid, af);
-        }
+        frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
       }
-      writeDatasetFor(dsses, fileName, jout);
+      else
+      {
+        frames.add(af);
+      }
+      saveAllFrames(frames, jout);
       try
       {
         jout.flush();
@@ -686,21 +735,22 @@ public class Jalview2XML
   {
     if (viewIds == null)
     {
-      viewIds = new ArrayList<String>();
+      viewIds = new ArrayList<>();
     }
 
     initSeqRefs();
 
-    List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
+    List<UserColourScheme> userColours = new ArrayList<>();
 
     AlignViewport av = ap.av;
+    ViewportRanges vpRanges = av.getRanges();
 
     JalviewModel object = new JalviewModel();
     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
 
     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
-    object.setVersion(jalview.bin.Cache.getDefault("VERSION",
-            "Development Build"));
+    object.setVersion(
+            jalview.bin.Cache.getDefault("VERSION", "Development Build"));
 
     /**
      * rjal is full height alignment, jal is actual alignment with full metadata
@@ -744,38 +794,43 @@ public class Jalview2XML
     }
 
     JSeq jseq;
-    Set<String> calcIdSet = new HashSet<String>();
-
+    Set<String> calcIdSet = new HashSet<>();
+    // record the set of vamsas sequence XML POJO we create.
+    HashMap<String, Sequence> vamsasSetIds = new HashMap<>();
     // SAVE SEQUENCES
     for (final SequenceI jds : rjal.getSequences())
     {
       final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
               : jds.getDatasetSequence();
       String id = seqHash(jds);
-
-      if (seqRefIds.get(id) != null)
-      {
-        // This happens for two reasons: 1. multiple views are being serialised.
-        // 2. the hashCode has collided with another sequence's code. This DOES
-        // HAPPEN! (PF00072.15.stk does this)
-        // JBPNote: Uncomment to debug writing out of files that do not read
-        // back in due to ArrayOutOfBoundExceptions.
-        // System.err.println("vamsasSeq backref: "+id+"");
-        // System.err.println(jds.getName()+"
-        // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
-        // System.err.println("Hashcode: "+seqHash(jds));
-        // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
-        // System.err.println(rsq.getName()+"
-        // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
-        // System.err.println("Hashcode: "+seqHash(rsq));
-      }
-      else
-      {
-        vamsasSeq = createVamsasSequence(id, jds);
-        vamsasSet.addSequence(vamsasSeq);
-        seqRefIds.put(id, jds);
+      if (vamsasSetIds.get(id) == null)
+      {
+        if (seqRefIds.get(id) != null && !storeDS)
+        {
+          // This happens for two reasons: 1. multiple views are being
+          // serialised.
+          // 2. the hashCode has collided with another sequence's code. This
+          // DOES
+          // HAPPEN! (PF00072.15.stk does this)
+          // JBPNote: Uncomment to debug writing out of files that do not read
+          // back in due to ArrayOutOfBoundExceptions.
+          // System.err.println("vamsasSeq backref: "+id+"");
+          // System.err.println(jds.getName()+"
+          // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
+          // System.err.println("Hashcode: "+seqHash(jds));
+          // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
+          // System.err.println(rsq.getName()+"
+          // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
+          // System.err.println("Hashcode: "+seqHash(rsq));
+        }
+        else
+        {
+          vamsasSeq = createVamsasSequence(id, jds);
+          vamsasSet.addSequence(vamsasSeq);
+          vamsasSetIds.put(id, vamsasSeq);
+          seqRefIds.put(id, jds);
+        }
       }
-
       jseq = new JSeq();
       jseq.setStart(jds.getStart());
       jseq.setEnd(jds.getEnd());
@@ -788,14 +843,13 @@ public class Jalview2XML
         if (av.hasHiddenRows())
         {
           // use rjal, contains the full height alignment
-          jseq.setHidden(av.getAlignment().getHiddenSequences()
-                  .isHidden(jds));
+          jseq.setHidden(
+                  av.getAlignment().getHiddenSequences().isHidden(jds));
 
           if (av.isHiddenRepSequence(jds))
           {
             jalview.datamodel.SequenceI[] reps = av
-                    .getRepresentedSequences(jds)
-                    .getSequencesInOrder(rjal);
+                    .getRepresentedSequences(jds).getSequencesInOrder(rjal);
 
             for (int h = 0; h < reps.length; h++)
             {
@@ -815,48 +869,61 @@ public class Jalview2XML
 
       // TODO: omit sequence features from each alignment view's XML dump if we
       // are storing dataset
-      if (jds.getSequenceFeatures() != null)
+      List<jalview.datamodel.SequenceFeature> sfs = jds
+              .getSequenceFeatures();
+      for (SequenceFeature sf : sfs)
       {
-        jalview.datamodel.SequenceFeature[] sf = jds.getSequenceFeatures();
-        int index = 0;
-        while (index < sf.length)
-        {
-          Features features = new Features();
+        Features features = new Features();
 
-          features.setBegin(sf[index].getBegin());
-          features.setEnd(sf[index].getEnd());
-          features.setDescription(sf[index].getDescription());
-          features.setType(sf[index].getType());
-          features.setFeatureGroup(sf[index].getFeatureGroup());
-          features.setScore(sf[index].getScore());
-          if (sf[index].links != null)
+        features.setBegin(sf.getBegin());
+        features.setEnd(sf.getEnd());
+        features.setDescription(sf.getDescription());
+        features.setType(sf.getType());
+        features.setFeatureGroup(sf.getFeatureGroup());
+        features.setScore(sf.getScore());
+        if (sf.links != null)
+        {
+          for (int l = 0; l < sf.links.size(); l++)
           {
-            for (int l = 0; l < sf[index].links.size(); l++)
-            {
-              OtherData keyValue = new OtherData();
-              keyValue.setKey("LINK_" + l);
-              keyValue.setValue(sf[index].links.elementAt(l).toString());
-              features.addOtherData(keyValue);
-            }
+            OtherData keyValue = new OtherData();
+            keyValue.setKey("LINK_" + l);
+            keyValue.setValue(sf.links.elementAt(l).toString());
+            features.addOtherData(keyValue);
           }
-          if (sf[index].otherDetails != null)
+        }
+        if (sf.otherDetails != null)
+        {
+          /*
+           * save feature attributes, which may be simple strings or
+           * map valued (have sub-attributes)
+           */
+          for (Entry<String, Object> entry : sf.otherDetails.entrySet())
           {
-            String key;
-            Iterator<String> keys = sf[index].otherDetails.keySet()
-                    .iterator();
-            while (keys.hasNext())
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof Map<?, ?>)
             {
-              key = keys.next();
-              OtherData keyValue = new OtherData();
-              keyValue.setKey(key);
-              keyValue.setValue(sf[index].otherDetails.get(key).toString());
-              features.addOtherData(keyValue);
+              for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
+                      .entrySet())
+              {
+                OtherData otherData = new OtherData();
+                otherData.setKey(key);
+                otherData.setKey2(subAttribute.getKey());
+                otherData.setValue(subAttribute.getValue().toString());
+                features.addOtherData(otherData);
+              }
+            }
+            else
+            {
+              OtherData otherData = new OtherData();
+              otherData.setKey(key);
+              otherData.setValue(value.toString());
+              features.addOtherData(otherData);
             }
           }
-
-          jseq.addFeatures(features);
-          index++;
         }
+
+        jseq.addFeatures(features);
       }
 
       if (jdatasq.getAllPDBEntries() != null)
@@ -885,8 +952,8 @@ public class Jalview2XML
             if (frames[f] instanceof StructureViewerBase)
             {
               StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
-              matchedFile = saveStructureState(ap, jds, pdb, entry,
-                      viewIds, matchedFile, viewFrame);
+              matchedFile = saveStructureState(ap, jds, pdb, entry, viewIds,
+                      matchedFile, viewFrame);
               /*
                * Only store each structure viewer's state once in the project
                * jar. First time through only (storeDS==false)
@@ -902,8 +969,8 @@ public class Jalview2XML
                           viewerState.getBytes());
                 } catch (IOException e)
                 {
-                  System.err.println("Error saving viewer state: "
-                          + e.getMessage());
+                  System.err.println(
+                          "Error saving viewer state: " + e.getMessage());
                 }
               }
             }
@@ -919,7 +986,7 @@ public class Jalview2XML
             pdb.setFile(matchedFile); // entry.getFile());
             if (pdbfiles == null)
             {
-              pdbfiles = new ArrayList<String>();
+              pdbfiles = new ArrayList<>();
             }
 
             if (!pdbfiles.contains(pdbId))
@@ -929,17 +996,16 @@ public class Jalview2XML
             }
           }
 
-          if (entry.getProperty() != null && !entry.getProperty().isEmpty())
+          Enumeration<String> props = entry.getProperties();
+          if (props.hasMoreElements())
           {
             PdbentryItem item = new PdbentryItem();
-            Hashtable properties = entry.getProperty();
-            Enumeration en2 = properties.keys();
-            while (en2.hasMoreElements())
+            while (props.hasMoreElements())
             {
               Property prop = new Property();
-              String key = en2.nextElement().toString();
+              String key = props.nextElement();
               prop.setName(key);
-              prop.setValue(properties.get(key).toString());
+              prop.setValue(entry.getProperty(key).toString());
               item.addProperty(prop);
             }
             pdb.addPdbentryItem(item);
@@ -959,7 +1025,8 @@ public class Jalview2XML
       jal = av.getAlignment();
     }
     // SAVE MAPPINGS
-    if (jal.getCodonFrames() != null)
+    // FOR DATASET
+    if (storeDS && jal.getCodonFrames() != null)
     {
       List<AlignedCodonFrame> jac = jal.getCodonFrames();
       for (AlignedCodonFrame acf : jac)
@@ -975,8 +1042,8 @@ public class Jalview2XML
           {
             AlcodMap alcmap = new AlcodMap();
             alcmap.setDnasq(seqHash(dnas[m]));
-            alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
-                    false));
+            alcmap.setMapping(
+                    createVamsasMapping(pmaps[m], dnas[m], null, false));
             alc.addAlcodMap(alcmap);
             hasMap = true;
           }
@@ -1021,7 +1088,7 @@ public class Jalview2XML
 
     // SAVE TREES
     // /////////////////////////////////
-    if (!storeDS && av.currentTree != null)
+    if (!storeDS && av.getCurrentTree() != null)
     {
       // FIND ANY ASSOCIATED TREES
       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
@@ -1035,13 +1102,13 @@ public class Jalview2XML
           {
             TreePanel tp = (TreePanel) frames[t];
 
-            if (tp.treeCanvas.av.getAlignment() == jal)
+            if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
             {
               Tree tree = new Tree();
               tree.setTitle(tp.getTitle());
-              tree.setCurrentTree((av.currentTree == tp.getTree()));
-              tree.setNewick(tp.getTree().toString());
-              tree.setThreshold(tp.treeCanvas.threshold);
+              tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
+              tree.setNewick(tp.getTree().print());
+              tree.setThreshold(tp.getTreeCanvas().getThreshold());
 
               tree.setFitToWindow(tp.fitToWindow.getState());
               tree.setFontName(tp.getTreeFont().getName());
@@ -1068,7 +1135,7 @@ public class Jalview2XML
     /**
      * store forward refs from an annotationRow to any groups
      */
-    IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<SequenceGroup, String>();
+    IdentityHashMap<SequenceGroup, String> groupRefs = new IdentityHashMap<>();
     if (storeDS)
     {
       for (SequenceI sq : jal.getSequences())
@@ -1110,38 +1177,42 @@ public class Jalview2XML
           // group has references so set its ID field
           jGroup.setId(groupRefs.get(sg));
         }
-        if (sg.cs != null)
+        ColourSchemeI colourScheme = sg.getColourScheme();
+        if (colourScheme != null)
         {
-          if (sg.cs.conservationApplied())
+          ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
+          if (groupColourScheme.conservationApplied())
           {
-            jGroup.setConsThreshold(sg.cs.getConservationInc());
+            jGroup.setConsThreshold(groupColourScheme.getConservationInc());
 
-            if (sg.cs instanceof jalview.schemes.UserColourScheme)
+            if (colourScheme instanceof jalview.schemes.UserColourScheme)
             {
-              jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
+              jGroup.setColour(
+                      setUserColourScheme(colourScheme, userColours, jms));
             }
             else
             {
-              jGroup.setColour(ColourSchemeProperty.getColourName(sg.cs));
+              jGroup.setColour(colourScheme.getSchemeName());
             }
           }
-          else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
+          else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
           {
             jGroup.setColour("AnnotationColourGradient");
             jGroup.setAnnotationColours(constructAnnotationColours(
-                    (jalview.schemes.AnnotationColourGradient) sg.cs,
+                    (jalview.schemes.AnnotationColourGradient) colourScheme,
                     userColours, jms));
           }
-          else if (sg.cs instanceof jalview.schemes.UserColourScheme)
+          else if (colourScheme instanceof jalview.schemes.UserColourScheme)
           {
-            jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
+            jGroup.setColour(
+                    setUserColourScheme(colourScheme, userColours, jms));
           }
           else
           {
-            jGroup.setColour(ColourSchemeProperty.getColourName(sg.cs));
+            jGroup.setColour(colourScheme.getSchemeName());
           }
 
-          jGroup.setPidThreshold(sg.cs.getThreshold());
+          jGroup.setPidThreshold(groupColourScheme.getThreshold());
         }
 
         jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
@@ -1169,14 +1240,14 @@ public class Jalview2XML
       // /////////SAVE VIEWPORT
       Viewport view = new Viewport();
       view.setTitle(ap.alignFrame.getTitle());
-      view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
-              av.getSequenceSetId()));
+      view.setSequenceSetId(
+              makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
       view.setId(av.getViewId());
       if (av.getCodingComplement() != null)
       {
         view.setComplementId(av.getCodingComplement().getViewId());
       }
-      view.setViewName(av.viewName);
+      view.setViewName(av.getViewName());
       view.setGatheredViews(av.isGatherViewsHere());
 
       Rectangle size = ap.av.getExplodedGeometry();
@@ -1200,15 +1271,16 @@ public class Jalview2XML
       view.setWidth(size.width);
       view.setHeight(size.height);
 
-      view.setStartRes(av.startRes);
-      view.setStartSeq(av.startSeq);
+      view.setStartRes(vpRanges.getStartRes());
+      view.setStartSeq(vpRanges.getStartSeq());
 
       if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
         view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
                 userColours, jms));
       }
-      else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+      else if (av
+              .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
       {
         AnnotationColours ac = constructAnnotationColours(
                 (jalview.schemes.AnnotationColourGradient) av
@@ -1220,27 +1292,24 @@ public class Jalview2XML
       }
       else
       {
-        view.setBgColour(ColourSchemeProperty.getColourName(av
-                .getGlobalColourScheme()));
+        view.setBgColour(ColourSchemeProperty
+                .getColourName(av.getGlobalColourScheme()));
       }
 
+      ResidueShaderI vcs = av.getResidueShading();
       ColourSchemeI cs = av.getGlobalColourScheme();
 
       if (cs != null)
       {
-        if (cs.conservationApplied())
+        if (vcs.conservationApplied())
         {
-          view.setConsThreshold(cs.getConservationInc());
+          view.setConsThreshold(vcs.getConservationInc());
           if (cs instanceof jalview.schemes.UserColourScheme)
           {
             view.setBgColour(setUserColourScheme(cs, userColours, jms));
           }
         }
-
-        if (cs instanceof ResidueColourScheme)
-        {
-          view.setPidThreshold(cs.getThreshold());
-        }
+        view.setPidThreshold(vcs.getThreshold());
       }
 
       view.setConservationSelected(av.getConservationSelected());
@@ -1276,20 +1345,33 @@ public class Jalview2XML
       {
         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-        String[] renderOrder = ap.getSeqPanel().seqCanvas
-                .getFeatureRenderer().getRenderOrder()
-                .toArray(new String[0]);
+        FeatureRenderer fr = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer();
+        String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
 
-        Vector<String> settingsAdded = new Vector<String>();
+        Vector<String> settingsAdded = new Vector<>();
         if (renderOrder != null)
         {
           for (String featureType : renderOrder)
           {
-            FeatureColourI fcol = ap.getSeqPanel().seqCanvas
-                    .getFeatureRenderer()
-                    .getFeatureStyle(featureType);
             Setting setting = new Setting();
             setting.setType(featureType);
+
+            /*
+             * save any filter for the feature type
+             */
+            FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+            if (filter != null)  {
+              Iterator<FeatureMatcherI> filters = filter.getMatchers().iterator();
+              FeatureMatcherI firstFilter = filters.next();
+              setting.setMatcherSet(Jalview2XML.marshalFilter(
+                      firstFilter, filters, filter.isAnded()));
+            }
+
+            /*
+             * save colour scheme for the feature type
+             */
+            FeatureColourI fcol = fr.getFeatureStyle(featureType);
             if (!fcol.isSimpleColour())
             {
               setting.setColour(fcol.getMaxColour().getRGB());
@@ -1297,8 +1379,25 @@ public class Jalview2XML
               setting.setMin(fcol.getMin());
               setting.setMax(fcol.getMax());
               setting.setColourByLabel(fcol.isColourByLabel());
+              if (fcol.isColourByAttribute())
+              {
+                setting.setAttributeName(fcol.getAttributeName());
+              }
               setting.setAutoScale(fcol.isAutoScaled());
               setting.setThreshold(fcol.getThreshold());
+              Color noColour = fcol.getNoColour();
+              if (noColour == null)
+              {
+                setting.setNoValueColour(NoValueColour.NONE);
+              }
+              else if (noColour.equals(fcol.getMaxColour()))
+              {
+                setting.setNoValueColour(NoValueColour.MAX);
+              }
+              else
+              {
+                setting.setNoValueColour(NoValueColour.MIN);
+              }
               // -1 = No threshold, 0 = Below, 1 = Above
               setting.setThreshstate(fcol.isAboveThreshold() ? 1
                       : (fcol.isBelowThreshold() ? 0 : -1));
@@ -1308,9 +1407,9 @@ public class Jalview2XML
               setting.setColour(fcol.getColour().getRGB());
             }
 
-            setting.setDisplay(av.getFeaturesDisplayed().isVisible(
-                    featureType));
-            float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+            setting.setDisplay(
+                    av.getFeaturesDisplayed().isVisible(featureType));
+            float rorder = fr
                     .getOrder(featureType);
             if (rorder > -1)
             {
@@ -1322,10 +1421,8 @@ public class Jalview2XML
         }
 
         // is groups actually supposed to be a map here ?
-        Iterator<String> en = ap.getSeqPanel().seqCanvas
-                .getFeatureRenderer()
-                .getFeatureGroups().iterator();
-        Vector<String> groupsAdded = new Vector<String>();
+        Iterator<String> en = fr.getFeatureGroups().iterator();
+        Vector<String> groupsAdded = new Vector<>();
         while (en.hasNext())
         {
           String grp = en.next();
@@ -1335,9 +1432,8 @@ public class Jalview2XML
           }
           Group g = new Group();
           g.setName(grp);
-          g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
-                  .getFeatureRenderer().checkGroupVisibility(grp, false))
-                  .booleanValue());
+          g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
+                          .booleanValue());
           fs.addGroup(g);
           groupsAdded.addElement(grp);
         }
@@ -1346,18 +1442,18 @@ public class Jalview2XML
 
       if (av.hasHiddenColumns())
       {
-        if (av.getColumnSelection() == null
-                || av.getColumnSelection().getHiddenColumns() == null)
+        jalview.datamodel.HiddenColumns hidden = av.getAlignment()
+                .getHiddenColumns();
+        if (hidden == null)
         {
           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++)
+          Iterator<int[]> hiddenRegions = hidden.iterator();
+          while (hiddenRegions.hasNext())
           {
-            int[] region = av.getColumnSelection().getHiddenColumns()
-                    .get(c);
+            int[] region = hiddenRegions.next();
             HiddenColumns hc = new HiddenColumns();
             hc.setStart(region[0]);
             hc.setEnd(region[1]);
@@ -1396,8 +1492,8 @@ public class Jalview2XML
         System.out.println("Writing jar entry " + fileName);
         JarEntry entry = new JarEntry(fileName);
         jout.putNextEntry(entry);
-        PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
-                UTF_8));
+        PrintWriter pout = new PrintWriter(
+                new OutputStreamWriter(jout, UTF_8));
         Marshaller marshaller = new Marshaller(pout);
         marshaller.marshal(object);
         pout.flush();
@@ -1598,8 +1694,8 @@ public class Jalview2XML
       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())))
+              && !(entry.getId().length() > 4 && entry.getId().toLowerCase()
+                      .startsWith(pdbId.toLowerCase())))
       {
         /*
          * not interested in a binding to a different PDB entry here
@@ -1612,8 +1708,8 @@ public class Jalview2XML
       }
       else if (!matchedFile.equals(pdbentry.getFile()))
       {
-        Cache.log
-                .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
+        Cache.log.warn(
+                "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
                         + pdbentry.getFile());
       }
       // record the
@@ -1622,7 +1718,8 @@ public class Jalview2XML
       // match is ambiguous (e.g.
       // 1QIP==1qipA)
 
-      for (int smap = 0; smap < viewFrame.getBinding().getSequence()[peid].length; smap++)
+      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])
@@ -1646,6 +1743,15 @@ public class Jalview2XML
     return matchedFile;
   }
 
+  /**
+   * Populates the AnnotationColours xml for save. This captures the settings of
+   * the options in the 'Colour by Annotation' dialog.
+   * 
+   * @param acg
+   * @param userColours
+   * @param jms
+   * @return
+   */
   private AnnotationColours constructAnnotationColours(
           AnnotationColourGradient acg, List<UserColourScheme> userColours,
           JalviewModelSequence jms)
@@ -1653,16 +1759,17 @@ public class Jalview2XML
     AnnotationColours ac = new AnnotationColours();
     ac.setAboveThreshold(acg.getAboveThreshold());
     ac.setThreshold(acg.getAnnotationThreshold());
-    ac.setAnnotation(acg.getAnnotation());
-    if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+    // 2.10.2 save annotationId (unique) not annotation label
+    ac.setAnnotation(acg.getAnnotation().annotationId);
+    if (acg.getBaseColour() instanceof UserColourScheme)
     {
-      ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
-              userColours, jms));
+      ac.setColourScheme(
+              setUserColourScheme(acg.getBaseColour(), userColours, jms));
     }
     else
     {
-      ac.setColourScheme(ColourSchemeProperty.getColourName(acg
-              .getBaseColour()));
+      ac.setColourScheme(
+              ColourSchemeProperty.getColourName(acg.getBaseColour()));
     }
 
     ac.setMaxColour(acg.getMaxColour().getRGB());
@@ -1705,11 +1812,10 @@ public class Jalview2XML
         if (groupIdr == null)
         {
           // make a locally unique String
-          groupRefs.put(
-                  annotation.groupRef,
+          groupRefs.put(annotation.groupRef,
                   groupIdr = ("" + System.currentTimeMillis()
-                          + annotation.groupRef.getName() + groupRefs
-                          .size()));
+                          + annotation.groupRef.getName()
+                          + groupRefs.size()));
         }
         an.setGroupRef(groupIdr.toString());
       }
@@ -1789,7 +1895,8 @@ public class Jalview2XML
           }
           if (annotation.annotations[a].displayCharacter != null)
           {
-            ae.setDisplayCharacter(annotation.annotations[a].displayCharacter);
+            ae.setDisplayCharacter(
+                    annotation.annotations[a].displayCharacter);
           }
 
           if (!Float.isNaN(annotation.annotations[a].value))
@@ -1800,8 +1907,8 @@ public class Jalview2XML
           ae.setPosition(a);
           if (annotation.annotations[a].secondaryStructure > ' ')
           {
-            ae.setSecondaryStructure(annotation.annotations[a].secondaryStructure
-                    + "");
+            ae.setSecondaryStructure(
+                    annotation.annotations[a].secondaryStructure + "");
           }
 
           if (annotation.annotations[a].colour != null
@@ -1863,8 +1970,8 @@ public class Jalview2XML
       // 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.setParameters(
+              settings.getWsParamFile().replace("\n", "|\\n|"));
       vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
       // todo - decide if updateImmediately is needed for any projects.
 
@@ -1924,8 +2031,8 @@ public class Jalview2XML
       }
     }
     throw new Error(MessageManager.formatMessage(
-            "error.unsupported_version_calcIdparam",
-            new Object[] { calcIdParam.toString() }));
+            "error.unsupported_version_calcIdparam", new Object[]
+            { calcIdParam.toString() }));
   }
 
   /**
@@ -2007,16 +2114,17 @@ public class Jalview2XML
     if (jds.getDatasetSequence() != null)
     {
       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
-      if (jds.getDatasetSequence().getDBRefs() != null)
-      {
-        dbrefs = jds.getDatasetSequence().getDBRefs();
-      }
     }
     else
     {
-      vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
+      // seqId==dsseqid so we can tell which sequences really are
       // dataset sequences only
+      vamsasSeq.setDsseqid(id);
       dbrefs = jds.getDBRefs();
+      if (parentseq == null)
+      {
+        parentseq = jds;
+      }
     }
     if (dbrefs != null)
     {
@@ -2068,38 +2176,32 @@ public class Jalview2XML
       if (jmp.getTo() != null)
       {
         MappingChoice mpc = new MappingChoice();
-        if (recurse
-                && (parentseq != jmp.getTo() || parentseq
-                        .getDatasetSequence() != jmp.getTo()))
+
+        // check/create ID for the sequence referenced by getTo()
+
+        String jmpid = "";
+        SequenceI ps = null;
+        if (parentseq != jmp.getTo()
+                && parentseq.getDatasetSequence() != jmp.getTo())
         {
-          mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
-                  jmp.getTo(), jds));
+          // chaining dbref rather than a handshaking one
+          jmpid = seqHash(ps = jmp.getTo());
         }
         else
         {
-          String jmpid = "";
-          SequenceI ps = null;
-          if (parentseq != jmp.getTo()
-                  && parentseq.getDatasetSequence() != jmp.getTo())
-          {
-            // chaining dbref rather than a handshaking one
-            jmpid = seqHash(ps = jmp.getTo());
-          }
-          else
-          {
-            jmpid = seqHash(ps = parentseq);
-          }
-          mpc.setDseqFor(jmpid);
-          if (!seqRefIds.containsKey(mpc.getDseqFor()))
-          {
-            jalview.bin.Cache.log.debug("creatign new DseqFor ID");
-            seqRefIds.put(mpc.getDseqFor(), ps);
-          }
-          else
-          {
-            jalview.bin.Cache.log.debug("reusing DseqFor ID");
-          }
+          jmpid = seqHash(ps = parentseq);
+        }
+        mpc.setDseqFor(jmpid);
+        if (!seqRefIds.containsKey(mpc.getDseqFor()))
+        {
+          jalview.bin.Cache.log.debug("creatign new DseqFor ID");
+          seqRefIds.put(mpc.getDseqFor(), ps);
         }
+        else
+        {
+          jalview.bin.Cache.log.debug("reusing DseqFor ID");
+        }
+
         mp.setMappingChoice(mpc);
       }
     }
@@ -2172,8 +2274,8 @@ public class Jalview2XML
 
     for (int i = 0; i < 24; i++)
     {
-      newColours[i] = new java.awt.Color(Integer.parseInt(colours
-              .getUserColourScheme().getColour(i).getRGB(), 16));
+      newColours[i] = new java.awt.Color(Integer.parseInt(
+              colours.getUserColourScheme().getColour(i).getRGB(), 16));
     }
 
     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
@@ -2184,8 +2286,9 @@ public class Jalview2XML
       newColours = new java.awt.Color[23];
       for (int i = 0; i < 23; i++)
       {
-        newColours[i] = new java.awt.Color(Integer.parseInt(colours
-                .getUserColourScheme().getColour(i + 24).getRGB(), 16));
+        newColours[i] = new java.awt.Color(Integer.parseInt(
+                colours.getUserColourScheme().getColour(i + 24).getRGB(),
+                16));
       }
       ucs.setLowerCaseColours(newColours);
     }
@@ -2218,7 +2321,7 @@ public class Jalview2XML
     try
     {
       // create list to store references for any new Jmol viewers created
-      newStructureViewers = new Vector<JalviewStructureDisplayI>();
+      newStructureViewers = new Vector<>();
       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
       // Workaround is to make sure caller implements the JarInputStreamProvider
       // interface
@@ -2226,6 +2329,7 @@ public class Jalview2XML
 
       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
       af = loadJalviewAlign(jprovider);
+      af.setMenusForViewport();
 
     } catch (MalformedURLException e)
     {
@@ -2311,7 +2415,8 @@ public class Jalview2XML
       initSeqRefs();
     }
     AlignFrame af = null, _af = null;
-    Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
+    IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
+    Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
     final String file = jprovider.getFilename();
     try
     {
@@ -2338,13 +2443,24 @@ public class Jalview2XML
           if (true) // !skipViewport(object))
           {
             _af = loadFromObject(object, file, true, jprovider);
-            if (object.getJalviewModelSequence().getViewportCount() > 0)
+            if (_af != null && object.getJalviewModelSequence()
+                    .getViewportCount() > 0)
             {
-              af = _af;
-              if (af.viewport.isGatherViewsHere())
+              if (af == null)
               {
-                gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+                // store a reference to the first view
+                af = _af;
               }
+              if (_af.viewport.isGatherViewsHere())
+              {
+                // if this is a gathered view, keep its reference since
+                // after gathering views, only this frame will remain
+                af = _af;
+                gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af);
+              }
+              // Save dataset to register mappings once all resolved
+              importedDatasets.put(af.viewport.getAlignment().getDataset(),
+                      af.viewport.getAlignment().getDataset());
             }
           }
           entryCount++;
@@ -2360,8 +2476,8 @@ public class Jalview2XML
     {
       ex.printStackTrace();
       errorMessage = "Couldn't locate Jalview XML file : " + file;
-      System.err.println("Exception whilst loading jalview XML file : "
-              + ex + "\n");
+      System.err.println(
+              "Exception whilst loading jalview XML file : " + ex + "\n");
     } catch (Exception ex)
     {
       System.err.println("Parsing as Jalview Version 2 file failed.");
@@ -2390,8 +2506,8 @@ public class Jalview2XML
       }
       ex.printStackTrace();
 
-      System.err.println("Exception whilst loading jalview XML file : "
-              + ex + "\n");
+      System.err.println(
+              "Exception whilst loading jalview XML file : " + ex + "\n");
     } catch (OutOfMemoryError e)
     {
       // Don't use the OOM Window here
@@ -2400,11 +2516,6 @@ public class Jalview2XML
       e.printStackTrace();
     }
 
-    if (Desktop.instance != null)
-    {
-      Desktop.instance.stopLoading();
-    }
-
     /*
      * Regather multiple views (with the same sequence set id) to the frame (if
      * any) that is flagged as the one to gather to, i.e. convert them to tabbed
@@ -2418,11 +2529,25 @@ public class Jalview2XML
     }
 
     restoreSplitFrames();
-
+    for (AlignmentI ds : importedDatasets.keySet())
+    {
+      if (ds.getCodonFrames() != null)
+      {
+        StructureSelectionManager
+                .getStructureSelectionManager(Desktop.instance)
+                .registerMappings(ds.getCodonFrames());
+      }
+    }
     if (errorMessage != null)
     {
       reportErrors();
     }
+
+    if (Desktop.instance != null)
+    {
+      Desktop.instance.stopLoading();
+    }
+
     return af;
   }
 
@@ -2433,9 +2558,9 @@ public class Jalview2XML
    */
   protected void restoreSplitFrames()
   {
-    List<SplitFrame> gatherTo = new ArrayList<SplitFrame>();
-    List<AlignFrame> addedToSplitFrames = new ArrayList<AlignFrame>();
-    Map<String, AlignFrame> dna = new HashMap<String, AlignFrame>();
+    List<SplitFrame> gatherTo = new ArrayList<>();
+    List<AlignFrame> addedToSplitFrames = new ArrayList<>();
+    Map<String, AlignFrame> dna = new HashMap<>();
 
     /*
      * Identify the DNA alignments
@@ -2467,6 +2592,8 @@ public class Jalview2XML
           SplitFrame sf = createSplitFrame(dnaFrame, af);
           addedToSplitFrames.add(dnaFrame);
           addedToSplitFrames.add(af);
+          dnaFrame.setMenusForViewport();
+          af.setMenusForViewport();
           if (af.viewport.isGatherViewsHere())
           {
             gatherTo.add(sf);
@@ -2488,6 +2615,7 @@ public class Jalview2XML
         Viewport view = candidate.getKey();
         Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
                 view.getHeight());
+        af.setMenusForViewport();
         System.err.println("Failed to restore view " + view.getTitle()
                 + " to split frame");
       }
@@ -2557,10 +2685,11 @@ public class Jalview2XML
           @Override
           public void run()
           {
-            JOptionPane.showInternalMessageDialog(Desktop.desktop,
-                    finalErrorMessage, "Error "
-                            + (saving ? "saving" : "loading")
-                            + " Jalview file", JOptionPane.WARNING_MESSAGE);
+            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                    finalErrorMessage,
+                    "Error " + (saving ? "saving" : "loading")
+                            + " Jalview file",
+                    JvOptionPane.WARNING_MESSAGE);
           }
         });
       }
@@ -2572,7 +2701,7 @@ public class Jalview2XML
     errorMessage = null;
   }
 
-  Map<String, String> alreadyLoadedPDB = new HashMap<String, String>();
+  Map<String, String> alreadyLoadedPDB = new HashMap<>();
 
   /**
    * when set, local views will be updated from view stored in JalviewXML
@@ -2591,14 +2720,16 @@ public class Jalview2XML
    * @param pdbId
    * @return
    */
-  String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
+  String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
+          String origFile)
   {
     if (alreadyLoadedPDB.containsKey(pdbId))
     {
       return alreadyLoadedPDB.get(pdbId).toString();
     }
 
-    String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb");
+    String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
+            origFile);
     if (tempFile != null)
     {
       alreadyLoadedPDB.put(pdbId, tempFile);
@@ -2615,14 +2746,26 @@ public class Jalview2XML
    * @param prefix
    *          a prefix for the temporary file name, must be at least three
    *          characters long
+   * @param origFile
+   *          null or original file - so new file can be given the same suffix
+   *          as the old one
    * @return
    */
   protected String copyJarEntry(jarInputStreamProvider jprovider,
-          String jarEntryName, String prefix)
+          String jarEntryName, String prefix, String origFile)
   {
     BufferedReader in = null;
     PrintWriter out = null;
-
+    String suffix = ".tmp";
+    if (origFile == null)
+    {
+      origFile = jarEntryName;
+    }
+    int sfpos = origFile.lastIndexOf(".");
+    if (sfpos > -1 && sfpos < (origFile.length() - 3))
+    {
+      suffix = "." + origFile.substring(sfpos + 1);
+    }
     try
     {
       JarInputStream jin = jprovider.getJarInputStream();
@@ -2640,7 +2783,7 @@ public class Jalview2XML
       if (entry != null)
       {
         in = new BufferedReader(new InputStreamReader(jin, UTF_8));
-        File outFile = File.createTempFile(prefix, ".tmp");
+        File outFile = File.createTempFile(prefix, suffix);
         outFile.deleteOnExit();
         out = new PrintWriter(new FileOutputStream(outFile));
         String data;
@@ -2729,8 +2872,7 @@ public class Jalview2XML
 
     List<SequenceI> hiddenSeqs = null;
 
-
-    List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
+    List<SequenceI> tmpseqs = new ArrayList<>();
 
     boolean multipleView = false;
     SequenceI referenceseqForView = null;
@@ -2743,10 +2885,38 @@ public class Jalview2XML
       SequenceI tmpSeq = seqRefIds.get(seqId);
       if (tmpSeq != null)
       {
+        if (!incompleteSeqs.containsKey(seqId))
+        {
+          // may not need this check, but keep it for at least 2.9,1 release
+          if (tmpSeq.getStart() != jseqs[i].getStart()
+                  || tmpSeq.getEnd() != jseqs[i].getEnd())
+          {
+            System.err.println(
+                    "Warning JAL-2154 regression: updating start/end for sequence "
+                            + tmpSeq.toString() + " to " + jseqs[i]);
+          }
+        }
+        else
+        {
+          incompleteSeqs.remove(seqId);
+        }
+        if (vamsasSeq.length > vi && vamsasSeq[vi].getId().equals(seqId))
+        {
+          // most likely we are reading a dataset XML document so
+          // update from vamsasSeq section of XML for this sequence
+          tmpSeq.setName(vamsasSeq[vi].getName());
+          tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+          tmpSeq.setSequence(vamsasSeq[vi].getSequence());
+          vi++;
+        }
+        else
+        {
+          // reading multiple views, so vamsasSeq set is a subset of JSeq
+          multipleView = true;
+        }
         tmpSeq.setStart(jseqs[i].getStart());
         tmpSeq.setEnd(jseqs[i].getEnd());
         tmpseqs.add(tmpSeq);
-        multipleView = true;
       }
       else
       {
@@ -2770,7 +2940,7 @@ public class Jalview2XML
       {
         if (hiddenSeqs == null)
         {
-          hiddenSeqs = new ArrayList<SequenceI>();
+          hiddenSeqs = new ArrayList<>();
         }
 
         hiddenSeqs.add(tmpSeq);
@@ -2783,7 +2953,40 @@ public class Jalview2XML
     SequenceI[] orderedSeqs = tmpseqs
             .toArray(new SequenceI[tmpseqs.size()]);
 
-    AlignmentI al = new Alignment(orderedSeqs);
+    AlignmentI al = null;
+    // so we must create or recover the dataset alignment before going further
+    // ///////////////////////////////
+    if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
+    {
+      // older jalview projects do not have a dataset - so creat alignment and
+      // dataset
+      al = new Alignment(orderedSeqs);
+      al.setDataset(null);
+    }
+    else
+    {
+      boolean isdsal = object.getJalviewModelSequence()
+              .getViewportCount() == 0;
+      if (isdsal)
+      {
+        // we are importing a dataset record, so
+        // recover reference to an alignment already materialsed as dataset
+        al = getDatasetFor(vamsasSet.getDatasetId());
+      }
+      if (al == null)
+      {
+        // materialse the alignment
+        al = new Alignment(orderedSeqs);
+      }
+      if (isdsal)
+      {
+        addDatasetRef(vamsasSet.getDatasetId(), al);
+      }
+
+      // finally, verify all data in vamsasSet is actually present in al
+      // passing on flag indicating if it is actually a stored dataset
+      recoverDatasetFor(vamsasSet, al, isdsal);
+    }
 
     if (referenceseqForView != null)
     {
@@ -2796,22 +2999,6 @@ public class Jalview2XML
       al.setProperty(ssp.getKey(), ssp.getValue());
     }
 
-    // /
-    // SequenceFeatures are added to the DatasetSequence,
-    // so we must create or recover the dataset before loading features
-    // ///////////////////////////////
-    if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
-    {
-      // older jalview projects do not have a dataset id.
-      al.setDataset(null);
-    }
-    else
-    {
-      // 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);
-    }
     // ///////////////////////////////
 
     Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
@@ -2819,6 +3006,12 @@ public class Jalview2XML
     {
       // load sequence features, database references and any associated PDB
       // structures for the alignment
+      //
+      // prior to 2.10, this part would only be executed the first time a
+      // sequence was encountered, but not afterwards.
+      // now, for 2.10 projects, this is also done if the xml doc includes
+      // dataset sequences not actually present in any particular view.
+      //
       for (int i = 0; i < vamsasSeq.length; i++)
       {
         if (jseqs[i].getFeaturesCount() > 0)
@@ -2826,32 +3019,63 @@ public class Jalview2XML
           Features[] features = jseqs[i].getFeatures();
           for (int f = 0; f < features.length; f++)
           {
-            jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
-                    features[f].getType(), features[f].getDescription(),
-                    features[f].getStatus(), features[f].getBegin(),
-                    features[f].getEnd(), features[f].getFeatureGroup());
+            SequenceFeature sf = new SequenceFeature(features[f].getType(),
+                    features[f].getDescription(), features[f].getBegin(),
+                    features[f].getEnd(), features[f].getScore(),
+                    features[f].getFeatureGroup());
+            sf.setStatus(features[f].getStatus());
 
-            sf.setScore(features[f].getScore());
+            /*
+             * load any feature attributes - include map-valued attributes
+             */
+            Map<String, Map<String, String>> mapAttributes = new HashMap<>();
             for (int od = 0; od < features[f].getOtherDataCount(); od++)
             {
               OtherData keyValue = features[f].getOtherData(od);
-              if (keyValue.getKey().startsWith("LINK"))
+              String attributeName = keyValue.getKey();
+              String attributeValue = keyValue.getValue();
+              if (attributeName.startsWith("LINK"))
               {
-                sf.addLink(keyValue.getValue());
+                sf.addLink(attributeValue);
               }
               else
               {
-                sf.setValue(keyValue.getKey(), keyValue.getValue());
+                String subAttribute = keyValue.getKey2();
+                if (subAttribute == null)
+                {
+                  // simple string-valued attribute
+                  sf.setValue(attributeName, attributeValue);
+                }
+                else
+                {
+                  // attribute 'key' has sub-attribute 'key2'
+                  if (!mapAttributes.containsKey(attributeName))
+                  {
+                    mapAttributes.put(attributeName, new HashMap<>());
+                  }
+                  mapAttributes.get(attributeName).put(subAttribute,
+                          attributeValue);
+                }
               }
-
+            }
+            for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
+                    .entrySet())
+            {
+              sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
             }
 
-            al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
+            // adds feature to datasequence's feature set (since Jalview 2.10)
+            al.getSequenceAt(i).addSequenceFeature(sf);
           }
         }
         if (vamsasSeq[i].getDBRefCount() > 0)
         {
-          addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
+          // adds dbrefs to datasequence's set (since Jalview 2.10)
+          addDBRefs(
+                  al.getSequenceAt(i).getDatasetSequence() == null
+                          ? al.getSequenceAt(i)
+                          : al.getSequenceAt(i).getDatasetSequence(),
+                  vamsasSeq[i]);
         }
         if (jseqs[i].getPdbidsCount() > 0)
         {
@@ -2862,29 +3086,50 @@ public class Jalview2XML
             entry.setId(ids[p].getId());
             if (ids[p].getType() != null)
             {
-              if (ids[p].getType().equalsIgnoreCase("PDB"))
+              if (PDBEntry.Type.getType(ids[p].getType()) != null)
               {
-                entry.setType(PDBEntry.Type.PDB);
+                entry.setType(PDBEntry.Type.getType(ids[p].getType()));
               }
               else
               {
                 entry.setType(PDBEntry.Type.FILE);
               }
             }
-            if (ids[p].getFile() != null)
+            // jprovider is null when executing 'New View'
+            if (ids[p].getFile() != null && jprovider != null)
             {
               if (!pdbloaded.containsKey(ids[p].getFile()))
               {
-                entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
+                entry.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                        ids[p].getFile()));
               }
               else
               {
                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
               }
             }
-            StructureSelectionManager.getStructureSelectionManager(
-                    Desktop.instance).registerPDBEntry(entry);
-            al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+            if (ids[p].getPdbentryItem() != null)
+            {
+              for (PdbentryItem item : ids[p].getPdbentryItem())
+              {
+                for (Property pr : item.getProperty())
+                {
+                  entry.setProperty(pr.getName(), pr.getValue());
+                }
+              }
+            }
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.instance)
+                    .registerPDBEntry(entry);
+            // adds PDBEntry to datasequence's set (since Jalview 2.10)
+            if (al.getSequenceAt(i).getDatasetSequence() != null)
+            {
+              al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+            }
+            else
+            {
+              al.getSequenceAt(i).addPDBId(entry);
+            }
           }
         }
       }
@@ -2913,16 +3158,16 @@ public class Jalview2XML
             if (maps[m].getMapping() != null)
             {
               mapping = addMapping(maps[m].getMapping());
-            }
-            if (dnaseq != null && mapping.getTo() != null)
-            {
-              cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
-            }
-            else
-            {
-              // defer to later
-              frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf,
-                      mapping));
+              if (dnaseq != null && mapping.getTo() != null)
+              {
+                cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
+              }
+              else
+              {
+                // defer to later
+                frefedSequence.add(
+                        newAlcodMapRef(maps[m].getDnasq(), cf, mapping));
+              }
             }
           }
           al.addCodonFrame(cf);
@@ -2932,12 +3177,12 @@ public class Jalview2XML
 
     // ////////////////////////////////
     // LOAD ANNOTATIONS
-    List<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
+    List<JvAnnotRow> autoAlan = new ArrayList<>();
 
     /*
      * store any annotations which forward reference a group's ID
      */
-    Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, List<AlignmentAnnotation>>();
+    Map<String, List<AlignmentAnnotation>> groupAnnotRefs = new Hashtable<>();
 
     if (vamsasSet.getAnnotationCount() > 0)
     {
@@ -2962,9 +3207,8 @@ public class Jalview2XML
             annotation.setAutoCalculated(true);
           }
         }
-        if (autoForView
-                || (annotation.hasAutoCalculated() && annotation
-                        .isAutoCalculated()))
+        if (autoForView || (annotation.hasAutoCalculated()
+                && annotation.isAutoCalculated()))
         {
           // remove ID - we don't recover annotation from other views for
           // view-specific annotation
@@ -3006,10 +3250,12 @@ public class Jalview2XML
 
             anot[anpos] = new jalview.datamodel.Annotation(
 
-            ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
-                    (ae[aa].getSecondaryStructure() == null || ae[aa]
-                            .getSecondaryStructure().length() == 0) ? ' '
-                            : ae[aa].getSecondaryStructure().charAt(0),
+                    ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
+                    (ae[aa].getSecondaryStructure() == null
+                            || ae[aa].getSecondaryStructure().length() == 0)
+                                    ? ' '
+                                    : ae[aa].getSecondaryStructure()
+                                            .charAt(0),
                     ae[aa].getValue()
 
             );
@@ -3043,10 +3289,11 @@ public class Jalview2XML
           jaa._linecolour = firstColour;
           if (annotation.getThresholdLine() != null)
           {
-            jaa.setThreshold(new jalview.datamodel.GraphLine(annotation
-                    .getThresholdLine().getValue(), annotation
-                    .getThresholdLine().getLabel(), new java.awt.Color(
-                    annotation.getThresholdLine().getColour())));
+            jaa.setThreshold(new jalview.datamodel.GraphLine(
+                    annotation.getThresholdLine().getValue(),
+                    annotation.getThresholdLine().getLabel(),
+                    new java.awt.Color(
+                            annotation.getThresholdLine().getColour())));
 
           }
           if (autoForView || annotation.isAutoCalculated())
@@ -3092,7 +3339,7 @@ public class Jalview2XML
                   .get(an[i].getGroupRef());
           if (aal == null)
           {
-            aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
+            aal = new ArrayList<>();
             groupAnnotRefs.put(an[i].getGroupRef(), aal);
           }
           aal.add(jaa);
@@ -3174,20 +3421,16 @@ public class Jalview2XML
                   && jGroup.getAnnotationColours() != null)
           {
             addAnnotSchemeGroup = true;
-            cs = null;
           }
           else
           {
-            cs = ColourSchemeProperty.getColour(al, jGroup.getColour());
-          }
-
-          if (cs != null)
-          {
-            cs.setThreshold(jGroup.getPidThreshold(), true);
+            cs = ColourSchemeProperty.getColourScheme(al,
+                    jGroup.getColour());
           }
         }
+        int pidThreshold = jGroup.getPidThreshold();
 
-        Vector<SequenceI> seqs = new Vector<SequenceI>();
+        Vector<SequenceI> seqs = new Vector<>();
 
         for (int s = 0; s < jGroup.getSeqCount(); s++)
         {
@@ -3208,13 +3451,16 @@ public class Jalview2XML
         SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
                 jGroup.getDisplayBoxes(), jGroup.getDisplayText(),
                 jGroup.getColourText(), jGroup.getStart(), jGroup.getEnd());
-
+        sg.getGroupColourScheme().setThreshold(pidThreshold, true);
+        sg.getGroupColourScheme()
+                .setConservationInc(jGroup.getConsThreshold());
         sg.setOutlineColour(new java.awt.Color(jGroup.getOutlineColour()));
 
         sg.textColour = new java.awt.Color(jGroup.getTextCol1());
         sg.textColour2 = new java.awt.Color(jGroup.getTextCol2());
-        sg.setShowNonconserved(jGroup.hasShowUnconserved() ? jGroup
-                .isShowUnconserved() : false);
+        sg.setShowNonconserved(
+                jGroup.hasShowUnconserved() ? jGroup.isShowUnconserved()
+                        : false);
         sg.thresholdTextColour = jGroup.getTextColThreshold();
         if (jGroup.hasShowConsensusHistogram())
         {
@@ -3235,9 +3481,8 @@ public class Jalview2XML
         }
         if (jGroup.getConsThreshold() != 0)
         {
-          jalview.analysis.Conservation c = new jalview.analysis.Conservation(
-                  "All", ResidueProperties.propHash, 3,
-                  sg.getSequences(null), 0, sg.getWidth() - 1);
+          Conservation c = new Conservation("All", sg.getSequences(null), 0,
+                  sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
@@ -3246,8 +3491,8 @@ public class Jalview2XML
         if (jGroup.getId() != null && groupAnnotRefs.size() > 0)
         {
           // re-instate unique group/annotation row reference
-          List<AlignmentAnnotation> jaal = groupAnnotRefs.get(jGroup
-                  .getId());
+          List<AlignmentAnnotation> jaal = groupAnnotRefs
+                  .get(jGroup.getId());
           if (jaal != null)
           {
             for (AlignmentAnnotation jaa : jaal)
@@ -3275,8 +3520,8 @@ public class Jalview2XML
         if (addAnnotSchemeGroup)
         {
           // reconstruct the annotation colourscheme
-          sg.cs = constructAnnotationColour(jGroup.getAnnotationColours(),
-                  null, al, jms, false);
+          sg.setColourScheme(constructAnnotationColour(
+                  jGroup.getAnnotationColours(), null, al, jms, false));
         }
       }
     }
@@ -3293,8 +3538,8 @@ public class Jalview2XML
     // to the same sequenceSet. We must modify this id on load
     // so that each load of the file gives a unique id
     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
-    String viewId = (view.getId() == null ? null : view.getId()
-            + uniqueSetSuffix);
+    String viewId = (view.getId() == null ? null
+            : view.getId() + uniqueSetSuffix);
     AlignFrame af = null;
     AlignViewport av = null;
     // now check to see if we really need to create a new viewport.
@@ -3307,8 +3552,8 @@ public class Jalview2XML
       // XML.
       // and then recover its containing af to allow the settings to be applied.
       // TODO: fix for vamsas demo
-      System.err
-              .println("About to recover a viewport for existing alignment: Sequence set ID is "
+      System.err.println(
+              "About to recover a viewport for existing alignment: Sequence set ID is "
                       + uniqueSeqSetId);
       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
       if (seqsetobj != null)
@@ -3316,14 +3561,14 @@ public class Jalview2XML
         if (seqsetobj instanceof String)
         {
           uniqueSeqSetId = (String) seqsetobj;
-          System.err
-                  .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
+          System.err.println(
+                  "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
                           + uniqueSeqSetId);
         }
         else
         {
-          System.err
-                  .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
+          System.err.println(
+                  "Warning : Collision between sequence set ID string and existing jalview object mapping.");
         }
 
       }
@@ -3332,8 +3577,8 @@ public class Jalview2XML
      * indicate that annotation colours are applied across all groups (pre
      * Jalview 2.8.1 behaviour)
      */
-    boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan(
-            "2.8.1", object.getVersion());
+    boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
+            object.getVersion());
 
     AlignmentPanel ap = null;
     boolean isnewview = true;
@@ -3408,15 +3653,15 @@ public class Jalview2XML
       for (int i = 0; i < jseq.getRnaViewerCount(); i++)
       {
         RnaViewer viewer = jseq.getRnaViewer(i);
-        AppVarna appVarna = findOrCreateVarnaViewer(viewer,
-                uniqueSetSuffix, ap);
+        AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix,
+                ap);
 
         for (int j = 0; j < viewer.getSecondaryStructureCount(); j++)
         {
           SecondaryStructure ss = viewer.getSecondaryStructure(j);
           SequenceI seq = seqRefIds.get(jseq.getId());
-          AlignmentAnnotation ann = this.annotationIds.get(ss
-                  .getAnnotationId());
+          AlignmentAnnotation ann = this.annotationIds
+                  .get(ss.getAnnotationId());
 
           /*
            * add the structure to the Varna display (with session state copied
@@ -3426,7 +3671,7 @@ public class Jalview2XML
           String rnaTitle = ss.getTitle();
           String sessionState = ss.getViewerState();
           String tempStateFile = copyJarEntry(jprovider, sessionState,
-                  "varna");
+                  "varna", null);
           RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
           appVarna.addModelSession(rna, rnaTitle, tempStateFile);
         }
@@ -3470,10 +3715,9 @@ public class Jalview2XML
     /*
      * viewer not found - make it
      */
-    RnaViewerModel model = new RnaViewerModel(postLoadId,
-            viewer.getTitle(), viewer.getXpos(), viewer.getYpos(),
-            viewer.getWidth(), viewer.getHeight(),
-            viewer.getDividerLocation());
+    RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(),
+            viewer.getXpos(), viewer.getYpos(), viewer.getWidth(),
+            viewer.getHeight(), viewer.getDividerLocation());
     AppVarna varna = new AppVarna(model, ap);
 
     return varna;
@@ -3502,7 +3746,7 @@ public class Jalview2XML
         TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
         if (tp == null)
         {
-          tp = af.ShowNewickTree(
+          tp = af.showNewickTree(
                   new jalview.io.NewickFile(tree.getNewick()),
                   tree.getTitle(), tree.getWidth(), tree.getHeight(),
                   tree.getXpos(), tree.getYpos());
@@ -3517,13 +3761,13 @@ public class Jalview2XML
           // TODO: should check if tp has been manipulated by user - if so its
           // settings shouldn't be modified
           tp.setTitle(tree.getTitle());
-          tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
-                  .getWidth(), tree.getHeight()));
-          tp.av = av; // af.viewport; // TODO: verify 'associate with all
+          tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(),
+                  tree.getWidth(), tree.getHeight()));
+          tp.setViewport(av); // af.viewport; // TODO: verify 'associate with all
           // views'
           // works still
-          tp.treeCanvas.av = av; // af.viewport;
-          tp.treeCanvas.ap = ap; // af.alignPanel;
+          tp.getTreeCanvas().setViewport(av); // af.viewport;
+          tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
 
         }
         if (tp == null)
@@ -3538,20 +3782,20 @@ public class Jalview2XML
 
         if (tree.getFontName() != null)
         {
-          tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
-                  .getFontStyle(), tree.getFontSize()));
+          tp.setTreeFont(new java.awt.Font(tree.getFontName(),
+                  tree.getFontStyle(), tree.getFontSize()));
         }
         else
         {
-          tp.setTreeFont(new java.awt.Font(view.getFontName(), view
-                  .getFontStyle(), tree.getFontSize()));
+          tp.setTreeFont(new java.awt.Font(view.getFontName(),
+                  view.getFontStyle(), tree.getFontSize()));
         }
 
         tp.showPlaceholders(tree.getMarkUnlinked());
         tp.showBootstrap(tree.getShowBootstrap());
         tp.showDistances(tree.getShowDistances());
 
-        tp.treeCanvas.threshold = tree.getThreshold();
+        tp.getTreeCanvas().setThreshold(tree.getThreshold());
 
         if (tree.getCurrentTree())
         {
@@ -3580,7 +3824,7 @@ public class Jalview2XML
      * 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>();
+    Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<>();
 
     for (int i = 0; i < jseqs.length; i++)
     {
@@ -3601,7 +3845,8 @@ public class Jalview2XML
             // 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.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                    ids[p].getFile()));
             jpdb.setId(ids[p].getId());
 
             int x = structureState.getXpos();
@@ -3612,13 +3857,14 @@ public class Jalview2XML
             // 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() + "");
+            String pdbFile = loadPDBFile(jprovider, ids[p].getId(),
+                    ids[p].getFile());
+            jalview.datamodel.SequenceI seq = seqRefIds
+                    .get(jseqs[i].getId() + "");
             if (sviewid == null)
             {
-              sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
-                      + "," + height;
+              sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + ","
+                      + height;
             }
             if (!structureViewers.containsKey(sviewid))
             {
@@ -3637,8 +3883,9 @@ public class Jalview2XML
             // linkAlignPanel,superposeWithAlignpanel}} from hash
             StructureViewerModel jmoldat = structureViewers.get(sviewid);
             jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
-                    | (structureState.hasAlignwithAlignPanel() ? structureState
-                            .getAlignwithAlignPanel() : false));
+                    | (structureState.hasAlignwithAlignPanel()
+                            ? structureState.getAlignwithAlignPanel()
+                            : false));
 
             /*
              * Default colour by linked panel to false if not specified (e.g.
@@ -3646,8 +3893,9 @@ public class Jalview2XML
              */
             boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
             colourWithAlignPanel |= (structureState
-                    .hasColourwithAlignPanel() ? structureState
-                    .getColourwithAlignPanel() : false);
+                    .hasColourwithAlignPanel()
+                            ? structureState.getColourwithAlignPanel()
+                            : false);
             jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
 
             /*
@@ -3655,8 +3903,9 @@ public class Jalview2XML
              * pre-2.7 projects)
              */
             boolean colourByViewer = jmoldat.isColourByViewer();
-            colourByViewer &= structureState.hasColourByJmol() ? structureState
-                    .getColourByJmol() : true;
+            colourByViewer &= structureState.hasColourByJmol()
+                    ? structureState.getColourByJmol()
+                    : true;
             jmoldat.setColourByViewer(colourByViewer);
 
             if (jmoldat.getStateData().length() < structureState
@@ -3672,8 +3921,7 @@ public class Jalview2XML
               StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
               if (seqstrmaps == null)
               {
-                jmoldat.getFileData().put(
-                        mapkey,
+                jmoldat.getFileData().put(mapkey,
                         seqstrmaps = jmoldat.new StructureData(pdbFile,
                                 ids[p].getId()));
               }
@@ -3701,8 +3949,8 @@ public class Jalview2XML
         createOrLinkStructureViewer(entry, af, ap, jprovider);
       } catch (Exception e)
       {
-        System.err.println("Error loading structure viewer: "
-                + e.getMessage());
+        System.err.println(
+                "Error loading structure viewer: " + e.getMessage());
         // failed - try the next one
       }
     }
@@ -3772,12 +4020,12 @@ public class Jalview2XML
      */
     String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
     chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
-            "chimera");
+            "chimera", null);
 
     Set<Entry<File, StructureData>> fileData = data.getFileData()
             .entrySet();
-    List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
-    List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
+    List<PDBEntry> pdbs = new ArrayList<>();
+    List<SequenceI[]> allseqs = new ArrayList<>();
     for (Entry<File, StructureData> pdb : fileData)
     {
       String filePath = pdb.getValue().getFilePath();
@@ -3794,8 +4042,8 @@ public class Jalview2XML
 
     // TODO use StructureViewer as a factory here, see JAL-1761
     final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
-    final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs
-            .size()][]);
+    final SequenceI[][] seqsArray = allseqs
+            .toArray(new SequenceI[allseqs.size()][]);
     String newViewId = viewerData.getKey();
 
     ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
@@ -3833,9 +4081,9 @@ public class Jalview2XML
               getViewerJarEntryName(svattrib.getViewId()));
     }
 
-    List<String> pdbfilenames = new ArrayList<String>();
-    List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
-    List<String> pdbids = new ArrayList<String>();
+    List<String> pdbfilenames = new ArrayList<>();
+    List<SequenceI[]> seqmaps = new ArrayList<>();
+    List<String> pdbids = new ArrayList<>();
     StringBuilder newFileLoc = new StringBuilder(64);
     int cp = 0, ncp, ecp;
     Map<File, StructureData> oldFiles = svattrib.getFileData();
@@ -3853,6 +4101,11 @@ public class Jalview2XML
         // filename
         // translation differently.
         StructureData filedat = oldFiles.get(new File(oldfilenam));
+        if (filedat == null)
+        {
+          String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
+          filedat = oldFiles.get(new File(reformatedOldFilename));
+        }
         newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
@@ -3901,8 +4154,8 @@ public class Jalview2XML
        */
       histbug += 10;
       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
-      String val = (diff == -1) ? null : newFileLoc
-              .substring(histbug, diff);
+      String val = (diff == -1) ? null
+              : newFileLoc.substring(histbug, diff);
       if (val != null && val.length() >= 4)
       {
         if (val.contains("e")) // eh? what can it be?
@@ -3920,8 +4173,8 @@ public class Jalview2XML
       }
     }
 
-    final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
-            .size()]);
+    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()][]);
@@ -3940,10 +4193,11 @@ public class Jalview2XML
           JalviewStructureDisplayI sview = null;
           try
           {
-            sview = new StructureViewer(alf.alignPanel
-                    .getStructureSelectionManager()).createView(
-                    StructureViewer.ViewerType.JMOL, pdbf, id, sq,
-                    alf.alignPanel, svattrib, fileloc, rect, sviewid);
+            sview = new StructureViewer(
+                    alf.alignPanel.getStructureSelectionManager())
+                            .createView(StructureViewer.ViewerType.JMOL,
+                                    pdbf, id, sq, alf.alignPanel, svattrib,
+                                    fileloc, rect, sviewid);
             addNewStructureViewer(sview);
           } catch (OutOfMemoryError ex)
           {
@@ -4003,9 +4257,8 @@ public class Jalview2XML
         /*
          * Post jalview 2.4 schema includes structure view id
          */
-        if (sviewid != null
-                && ((StructureViewerBase) frame).getViewId()
-                        .equals(sviewid))
+        if (sviewid != null && ((StructureViewerBase) frame).getViewId()
+                .equals(sviewid))
         {
           comp = (StructureViewerBase) frame;
           break; // break added in 2.9
@@ -4058,8 +4311,8 @@ public class Jalview2XML
       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.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
+              null);
       binding.addSequenceForStructFile(pdbFile, seq);
     }
     // and add the AlignmentPanel's reference to the view panel
@@ -4174,12 +4427,13 @@ public class Jalview2XML
     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
             uniqueSeqSetId, viewId);
 
-    af.setFileName(file, "Jalview");
+    af.setFileName(file, FileFormat.Jalview);
 
     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()));
     }
 
     if (al.hasSeqrep())
@@ -4219,8 +4473,8 @@ public class Jalview2XML
         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
         {
           isRepresentative = true;
-          SequenceI sequenceToHide = al.getSequenceAt(JSEQ[s]
-                  .getHiddenSequences(r));
+          SequenceI sequenceToHide = al
+                  .getSequenceAt(JSEQ[s].getHiddenSequences(r));
           hidden.addSequence(sequenceToHide, false);
           // remove from hiddenSeqs list so we don't try to hide it twice
           hiddenSeqs.remove(sequenceToHide);
@@ -4233,31 +4487,25 @@ public class Jalview2XML
         }
       }
 
-      SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
-              .size()]);
+      SequenceI[] hseqs = hiddenSeqs
+              .toArray(new SequenceI[hiddenSeqs.size()]);
       af.viewport.hideSequence(hseqs);
 
     }
     // recover view properties and display parameters
-    if (view.getViewName() != null)
-    {
-      af.viewport.viewName = view.getViewName();
-      af.setInitialTabVisible();
-    }
-    af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
-            view.getHeight());
 
     af.viewport.setShowAnnotation(view.getShowAnnotation());
     af.viewport.setAbovePIDThreshold(view.getPidSelected());
+    af.viewport.setThreshold(view.getPidThreshold());
 
     af.viewport.setColourText(view.getShowColourText());
 
     af.viewport.setConservationSelected(view.getConservationSelected());
+    af.viewport.setIncrement(view.getConsThreshold());
     af.viewport.setShowJVSuffix(view.getShowFullId());
     af.viewport.setRightAlignIds(view.getRightAlignIds());
-    af.viewport.setFont(
-            new java.awt.Font(view.getFontName(), view.getFontStyle(), view
-                    .getFontSize()), true);
+    af.viewport.setFont(new java.awt.Font(view.getFontName(),
+            view.getFontStyle(), view.getFontSize()), true);
     ViewStyleI vs = af.viewport.getViewStyle();
     vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna());
     af.viewport.setViewStyle(vs);
@@ -4274,10 +4522,18 @@ public class Jalview2XML
     af.viewport.setTextColour(new java.awt.Color(view.getTextCol1()));
     af.viewport.setTextColour2(new java.awt.Color(view.getTextCol2()));
     af.viewport.setThresholdTextColour(view.getTextColThreshold());
-    af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
-            .isShowUnconserved() : false);
-    af.viewport.setStartRes(view.getStartRes());
-    af.viewport.setStartSeq(view.getStartSeq());
+    af.viewport.setShowUnconserved(
+            view.hasShowUnconserved() ? view.isShowUnconserved() : false);
+    af.viewport.getRanges().setStartRes(view.getStartRes());
+
+    if (view.getViewName() != null)
+    {
+      af.viewport.setViewName(view.getViewName());
+      af.setInitialTabVisible();
+    }
+    af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
+            view.getHeight());
+    // startSeq set in af.alignPanel.updateLayout below
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
     // apply colourschemes
@@ -4297,22 +4553,21 @@ public class Jalview2XML
       }
       else
       {
-        cs = ColourSchemeProperty.getColour(al, view.getBgColour());
-      }
-
-      if (cs != null)
-      {
-        cs.setThreshold(view.getPidThreshold(), true);
-        cs.setConsensus(af.viewport.getSequenceConsensusHash());
+        cs = ColourSchemeProperty.getColourScheme(al, view.getBgColour());
       }
     }
 
     af.viewport.setGlobalColourScheme(cs);
+    af.viewport.getResidueShading().setThreshold(view.getPidThreshold(),
+            view.getIgnoreGapsinConsensus());
+    af.viewport.getResidueShading()
+            .setConsensus(af.viewport.getSequenceConsensusHash());
     af.viewport.setColourAppliesToAllGroups(false);
 
     if (view.getConservationSelected() && cs != null)
     {
-      cs.setConservationInc(view.getConsThreshold());
+      af.viewport.getResidueShading()
+              .setConservationInc(view.getConsThreshold());
     }
 
     af.changeColour(cs);
@@ -4340,8 +4595,8 @@ public class Jalview2XML
     }
     if (view.hasShowConsensusHistogram())
     {
-      af.viewport.setShowConsensusHistogram(view
-              .getShowConsensusHistogram());
+      af.viewport
+              .setShowConsensusHistogram(view.getShowConsensusHistogram());
     }
     else
     {
@@ -4384,27 +4639,67 @@ public class Jalview2XML
       af.viewport.setShowGroupConservation(false);
     }
 
-    // recover featre settings
+    // recover feature settings
     if (jms.getFeatureSettings() != null)
     {
+      FeatureRenderer fr = af.alignPanel.getSeqPanel().seqCanvas
+              .getFeatureRenderer();
       FeaturesDisplayed fdi;
       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
-      Map<String, FeatureColourI> featureColours = new Hashtable<String, FeatureColourI>();
-      Map<String, Float> featureOrder = new Hashtable<String, Float>();
+      Map<String, FeatureColourI> featureColours = new Hashtable<>();
+      Map<String, Float> featureOrder = new Hashtable<>();
 
-      for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
+      for (int fs = 0; fs < jms.getFeatureSettings()
+              .getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
+        String featureType = setting.getType();
+
+        /*
+         * restore feature filters (if any)
+         */
+        MatcherSet filters = setting.getMatcherSet();
+        if (filters != null)
+        {
+          FeatureMatcherSetI filter = Jalview2XML
+                  .unmarshalFilter(featureType, filters);
+          if (!filter.isEmpty())
+          {
+            fr.setFeatureFilter(featureType, filter);
+          }
+        }
+
+        /*
+         * restore feature colour scheme
+         */
+        Color maxColour = new Color(setting.getColour());
         if (setting.hasMincolour())
         {
-          FeatureColourI gc = setting.hasMin() ? new FeatureColour(
-                  new Color(setting.getMincolour()), new Color(
-                          setting.getColour()), setting.getMin(),
-                  setting.getMax()) : new FeatureColour(new Color(
-                  setting.getMincolour()), new Color(setting.getColour()),
-                  0, 1);
+          /*
+           * minColour is always set unless a simple colour
+           * (including for colour by label though it doesn't use it)
+           */
+          Color minColour = new Color(setting.getMincolour());
+          Color noValueColour = minColour;
+          NoValueColour noColour = setting.getNoValueColour();
+          if (noColour == NoValueColour.NONE)
+          {
+            noValueColour = null;
+          }
+          else if (noColour == NoValueColour.MAX)
+          {
+            noValueColour = maxColour;
+          }
+          float min = setting.hasMin() ? setting.getMin() : 0f;
+          float max = setting.hasMin() ? setting.getMax() : 1f;
+          FeatureColourI gc = new FeatureColour(minColour, maxColour,
+                  noValueColour, min, max);
+          if (setting.getAttributeNameCount() > 0)
+          {
+            gc.setAttributeName(setting.getAttributeName());
+          }
           if (setting.hasThreshold())
           {
             gc.setThreshold(setting.getThreshold());
@@ -4429,29 +4724,29 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          featureColours.put(setting.getType(), gc);
+          featureColours.put(featureType, gc);
         }
         else
         {
-          featureColours.put(setting.getType(), new FeatureColour(
-                  new Color(setting.getColour())));
+          featureColours.put(featureType,
+                  new FeatureColour(maxColour));
         }
-        renderOrder[fs] = setting.getType();
+        renderOrder[fs] = featureType;
         if (setting.hasOrder())
         {
-          featureOrder.put(setting.getType(), setting.getOrder());
+          featureOrder.put(featureType, setting.getOrder());
         }
         else
         {
-          featureOrder.put(setting.getType(), new Float(fs
-                  / jms.getFeatureSettings().getSettingCount()));
+          featureOrder.put(featureType, new Float(
+                  fs / jms.getFeatureSettings().getSettingCount()));
         }
         if (setting.getDisplay())
         {
-          fdi.setVisible(setting.getType());
+          fdi.setVisible(featureType);
         }
       }
-      Map<String, Boolean> fgtable = new Hashtable<String, Boolean>();
+      Map<String, Boolean> fgtable = new Hashtable<>();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
@@ -4460,20 +4755,18 @@ public class Jalview2XML
       // 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);
-
+      FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
+              fgtable, featureColours, 1.0f, featureOrder);
+      fr.transferSettings(frs);
     }
 
     if (view.getHiddenColumnsCount() > 0)
     {
       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
       {
-        af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
-                .getHiddenColumns(c).getEnd() // +1
-                );
+        af.viewport.hideColumns(view.getHiddenColumns(c).getStart(),
+                view.getHiddenColumns(c).getEnd() // +1
+        );
       }
     }
     if (view.getCalcIdParam() != null)
@@ -4494,7 +4787,7 @@ public class Jalview2XML
       }
     }
     af.setMenusFromViewport(af.viewport);
-
+    af.setTitle(view.getTitle());
     // TODO: we don't need to do this if the viewport is aready visible.
     /*
      * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
@@ -4518,12 +4811,21 @@ public class Jalview2XML
     return af;
   }
 
+  /**
+   * Reads saved data to restore Colour by Annotation settings
+   * 
+   * @param viewAnnColour
+   * @param af
+   * @param al
+   * @param jms
+   * @param checkGroupAnnColour
+   * @return
+   */
   private ColourSchemeI constructAnnotationColour(
           AnnotationColours viewAnnColour, AlignFrame af, AlignmentI 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)
@@ -4531,113 +4833,93 @@ public class Jalview2XML
       // pre 2.8.1 behaviour
       // check to see if we should transfer annotation colours
       propagateAnnColour = true;
-      for (jalview.datamodel.SequenceGroup sg : al.getGroups())
+      for (SequenceGroup sg : al.getGroups())
       {
-        if (sg.cs instanceof AnnotationColourGradient)
+        if (sg.getColourScheme() instanceof AnnotationColourGradient)
         {
           propagateAnnColour = false;
         }
       }
     }
-    // int find annotation
-    if (annAlignment.getAlignmentAnnotation() != null)
+
+    /*
+     * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId
+     */
+    String annotationId = viewAnnColour.getAnnotation();
+    AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId);
+
+    /*
+     * pre 2.10.2: saved annotationId is AlignmentAnnotation.label
+     */
+    if (matchedAnnotation == null
+            && annAlignment.getAlignmentAnnotation() != null)
     {
       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
       {
-        if (annAlignment.getAlignmentAnnotation()[i].label
-                .equals(viewAnnColour.getAnnotation()))
+        if (annotationId
+                .equals(annAlignment.getAlignmentAnnotation()[i].label))
         {
-          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;
-              }
+          matchedAnnotation = annAlignment.getAlignmentAnnotation()[i];
+          break;
+        }
+      }
+    }
+    if (matchedAnnotation == null)
+    {
+      System.err.println("Failed to match annotation colour scheme for "
+              + annotationId);
+      return null;
+    }
+    if (matchedAnnotation.getThreshold() == null)
+    {
+      matchedAnnotation.setThreshold(new GraphLine(
+              viewAnnColour.getThreshold(), "Threshold", Color.black));
+    }
 
-              /*
-               * 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());
-                  }
-                }
-              }
+    AnnotationColourGradient cs = null;
+    if (viewAnnColour.getColourScheme().equals("None"))
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation,
+              new Color(viewAnnColour.getMinColour()),
+              new Color(viewAnnColour.getMaxColour()),
+              viewAnnColour.getAboveThreshold());
+    }
+    else if (viewAnnColour.getColourScheme().startsWith("ucs"))
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation,
+              getUserColourScheme(jms, viewAnnColour.getColourScheme()),
+              viewAnnColour.getAboveThreshold());
+    }
+    else
+    {
+      cs = new AnnotationColourGradient(matchedAnnotation,
+              ColourSchemeProperty.getColourScheme(al,
+                      viewAnnColour.getColourScheme()),
+              viewAnnColour.getAboveThreshold());
+    }
 
-            }
-          }
+    boolean perSequenceOnly = viewAnnColour.isPerSequence();
+    boolean useOriginalColours = viewAnnColour.isPredefinedColours();
+    cs.setSeqAssociated(perSequenceOnly);
+    cs.setPredefinedColours(useOriginalColours);
 
-          break;
+    if (propagateAnnColour && al.getGroups() != null)
+    {
+      // Also use these settings for all the groups
+      for (int g = 0; g < al.getGroups().size(); g++)
+      {
+        SequenceGroup sg = al.getGroups().get(g);
+        if (sg.getGroupColourScheme() == null)
+        {
+          continue;
         }
 
+        AnnotationColourGradient groupScheme = new AnnotationColourGradient(
+                matchedAnnotation, sg.getColourScheme(),
+                viewAnnColour.getAboveThreshold());
+        sg.setColourScheme(groupScheme);
+        groupScheme.setSeqAssociated(perSequenceOnly);
+        groupScheme.setPredefinedColours(useOriginalColours);
       }
     }
     return cs;
@@ -4656,7 +4938,7 @@ public class Jalview2XML
       String[] magicNames = new String[] { "Consensus", "Quality",
           "Conservation" };
       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
-      Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
+      Hashtable<String, JvAnnotRow> visan = new Hashtable<>();
       for (String nm : magicNames)
       {
         visan.put(nm, nullAnnot);
@@ -4664,15 +4946,16 @@ public class Jalview2XML
       for (JvAnnotRow auan : autoAlan)
       {
         visan.put(auan.template.label
-                + (auan.template.getCalcId() == null ? "" : "\t"
-                        + auan.template.getCalcId()), auan);
+                + (auan.template.getCalcId() == null ? ""
+                        : "\t" + auan.template.getCalcId()),
+                auan);
       }
       int hSize = al.getAlignmentAnnotation().length;
-      List<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
+      List<JvAnnotRow> reorder = new ArrayList<>();
       // 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<String>(visan.keySet());
+      List<String> remains = new ArrayList<>(visan.keySet());
       for (int h = 0; h < hSize; h++)
       {
         jalview.datamodel.AlignmentAnnotation jalan = al
@@ -4768,8 +5051,9 @@ public class Jalview2XML
       return false;
     }
     String id;
-    if (skipList.containsKey(id = object.getJalviewModelSequence()
-            .getViewport()[0].getSequenceSetId()))
+    if (skipList.containsKey(
+            id = object.getJalviewModelSequence().getViewport()[0]
+                    .getSequenceSetId()))
     {
       if (Cache.log != null && Cache.log.isDebugEnabled())
       {
@@ -4801,8 +5085,8 @@ public class Jalview2XML
   private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
           boolean ignoreUnrefed)
   {
-    jalview.datamodel.AlignmentI ds = getDatasetFor(vamsasSet
-            .getDatasetId());
+    jalview.datamodel.AlignmentI ds = getDatasetFor(
+            vamsasSet.getDatasetId());
     Vector dseqs = null;
     if (ds == null)
     {
@@ -4812,7 +5096,7 @@ public class Jalview2XML
     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
     {
       Sequence vamsasSeq = vamsasSet.getSequence(i);
-      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
+      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i);
     }
     // create a new dataset
     if (ds == null)
@@ -4839,18 +5123,29 @@ public class Jalview2XML
    *          dataset alignment
    * @param dseqs
    *          vector to add new dataset sequence to
+   * @param ignoreUnrefed
+   *          - when true, don't create new sequences from vamsasSeq if it's id
+   *          doesn't already have an asssociated Jalview sequence.
+   * @param vseqpos
+   *          - used to reorder the sequence in the alignment according to the
+   *          vamsasSeq array ordering, to preserve ordering of dataset
    */
   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
+          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos)
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
     SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+    boolean reorder = false;
     SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
       dsq = sq.getDatasetSequence();
     }
+    else
+    {
+      reorder = true;
+    }
     if (sq == null && ignoreUnrefed)
     {
       return;
@@ -4940,12 +5235,41 @@ public class Jalview2XML
         }
         // 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 (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
+        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" : ""));
       }
     }
+    else
+    {
+      // sequence refs are identical. We may need to update the existing dataset
+      // alignment with this one, though.
+      if (ds != null && dseqs == null)
+      {
+        int opos = ds.findIndex(dsq);
+        SequenceI tseq = null;
+        if (opos != -1 && vseqpos != opos)
+        {
+          // remove from old position
+          ds.deleteSequence(dsq);
+        }
+        if (vseqpos < ds.getHeight())
+        {
+          if (vseqpos != opos)
+          {
+            // save sequence at destination position
+            tseq = ds.getSequenceAt(vseqpos);
+            ds.replaceSequenceAt(vseqpos, dsq);
+            ds.addSequence(tseq);
+          }
+        }
+        else
+        {
+          ds.addSequence(dsq);
+        }
+      }
+    }
   }
 
   /*
@@ -4960,7 +5284,7 @@ public class Jalview2XML
   {
     if (datasetIds == null)
     {
-      datasetIds = new Hashtable<String, AlignmentI>();
+      datasetIds = new Hashtable<>();
       return null;
     }
     if (datasetIds.containsKey(datasetId))
@@ -4974,7 +5298,7 @@ public class Jalview2XML
   {
     if (datasetIds == null)
     {
-      datasetIds = new Hashtable<String, AlignmentI>();
+      datasetIds = new Hashtable<>();
     }
     datasetIds.put(datasetId, dataset);
   }
@@ -4997,7 +5321,7 @@ public class Jalview2XML
       // make a new datasetId and record it
       if (dataset2Ids == null)
       {
-        dataset2Ids = new IdentityHashMap<AlignmentI, String>();
+        dataset2Ids = new IdentityHashMap<>();
       }
       else
       {
@@ -5018,8 +5342,9 @@ public class Jalview2XML
     {
       DBRef dr = sequence.getDBRef(d);
       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
-              sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
-                      .getVersion(), sequence.getDBRef(d).getAccessionId());
+              sequence.getDBRef(d).getSource(),
+              sequence.getDBRef(d).getVersion(),
+              sequence.getDBRef(d).getAccessionId());
       if (dr.getMapping() != null)
       {
         entry.setMap(addMapping(dr.getMapping()));
@@ -5048,8 +5373,8 @@ public class Jalview2XML
       fto[_i] = mf.getStart();
       fto[_i + 1] = mf.getEnd();
     }
-    jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
-            fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
+    jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto, fr,
+            fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
     if (m.getMappingChoice() != null)
     {
       MappingChoice mc = m.getMappingChoice();
@@ -5085,8 +5410,8 @@ public class Jalview2XML
         }
         else
         {
-          System.err
-                  .println("Warning - making up dataset sequence id for DbRef sequence map reference");
+          System.err.println(
+                  "Warning - making up dataset sequence id for DbRef sequence map reference");
           sqid = ((Object) ms).toString(); // make up a new hascode for
           // undefined dataset sequence hash
           // (unlikely to happen)
@@ -5103,6 +5428,7 @@ public class Jalview2XML
           djs.setEnd(jmap.getMap().getToHighest());
           djs.setVamsasId(uniqueSetSuffix + sqid);
           jmap.setTo(djs);
+          incompleteSeqs.put(sqid, djs);
           seqRefIds.put(sqid, djs);
 
         }
@@ -5115,28 +5441,25 @@ public class Jalview2XML
 
   }
 
-  public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
-          boolean keepSeqRefs)
+  /**
+   * Provides a 'copy' of an alignment view (on action New View) by 'saving' the
+   * view as XML (but not to file), and then reloading it
+   * 
+   * @param ap
+   * @return
+   */
+  public AlignmentPanel copyAlignPanel(AlignmentPanel ap)
   {
     initSeqRefs();
     JalviewModel jm = saveState(ap, null, null, null);
 
-    if (!keepSeqRefs)
-    {
-      clearSeqRefs();
-      jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
-    }
-    else
-    {
-      uniqueSetSuffix = "";
-      jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
-      // overwrite the
-      // view we just
-      // copied
-    }
+    uniqueSetSuffix = "";
+    jm.getJalviewModelSequence().getViewport(0).setId(null);
+    // we don't overwrite the view we just copied
+
     if (this.frefedSequence == null)
     {
-      frefedSequence = new Vector();
+      frefedSequence = new Vector<>();
     }
 
     viewportsAdded.clear();
@@ -5156,32 +5479,8 @@ public class Jalview2XML
     return af.alignPanel;
   }
 
-  /**
-   * flag indicating if hashtables should be cleared on finalization TODO this
-   * flag may not be necessary
-   */
-  private final boolean _cleartables = true;
-
   private Hashtable jvids2vobj;
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#finalize()
-   */
-  @Override
-  protected void finalize() throws Throwable
-  {
-    // really make sure we have no buried refs left.
-    if (_cleartables)
-    {
-      clearSeqRefs();
-    }
-    this.seqRefIds = null;
-    this.seqsToIds = null;
-    super.finalize();
-  }
-
   private void warn(String msg)
   {
     warn(msg, null);
@@ -5275,11 +5574,11 @@ public class Jalview2XML
         // register sequence object so the XML parser can recover it.
         if (seqRefIds == null)
         {
-          seqRefIds = new HashMap<String, SequenceI>();
+          seqRefIds = new HashMap<>();
         }
         if (seqsToIds == null)
         {
-          seqsToIds = new IdentityHashMap<SequenceI, String>();
+          seqsToIds = new IdentityHashMap<>();
         }
         seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
         seqsToIds.put((SequenceI) jvobj, id);
@@ -5412,4 +5711,289 @@ public class Jalview2XML
   {
     return counter++;
   }
+
+  /**
+   * Populates an XML model of the feature colour scheme for one feature type
+   * 
+   * @param featureType
+   * @param fcol
+   * @return
+   */
+  protected static jalview.schemabinding.version2.Colour marshalColour(
+          String featureType, FeatureColourI fcol)
+  {
+    jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
+    if (fcol.isSimpleColour())
+    {
+      col.setRGB(Format.getHexString(fcol.getColour()));
+    }
+    else
+    {
+      col.setRGB(Format.getHexString(fcol.getMaxColour()));
+      col.setMin(fcol.getMin());
+      col.setMax(fcol.getMax());
+      col.setMinRGB(jalview.util.Format.getHexString(fcol.getMinColour()));
+      col.setAutoScale(fcol.isAutoScaled());
+      col.setThreshold(fcol.getThreshold());
+      col.setColourByLabel(fcol.isColourByLabel());
+      col.setThreshType(fcol.isAboveThreshold() ? ColourThreshTypeType.ABOVE
+              : (fcol.isBelowThreshold() ? ColourThreshTypeType.BELOW
+                      : ColourThreshTypeType.NONE));
+      if (fcol.isColourByAttribute())
+      {
+        col.setAttributeName(fcol.getAttributeName());
+      }
+      Color noColour = fcol.getNoColour();
+      if (noColour == null)
+      {
+        col.setNoValueColour(NoValueColour.NONE);
+      }
+      else if (noColour == fcol.getMaxColour())
+      {
+        col.setNoValueColour(NoValueColour.MAX);
+      }
+      else
+      {
+        col.setNoValueColour(NoValueColour.MIN);
+      }
+    }
+    col.setName(featureType);
+    return col;
+  }
+
+  /**
+   * Populates an XML model of the feature filter(s) for one feature type
+   * 
+   * @param firstMatcher
+   *          the first (or only) match condition)
+   * @param filter
+   *          remaining match conditions (if any)
+   * @param and
+   *          if true, conditions are and-ed, else or-ed
+   */
+  protected static MatcherSet marshalFilter(FeatureMatcherI firstMatcher,
+          Iterator<FeatureMatcherI> filters, boolean and)
+  {
+    MatcherSet result = new MatcherSet();
+  
+    if (filters.hasNext())
+    {
+      /*
+       * compound matcher
+       */
+      CompoundMatcher compound = new CompoundMatcher();
+      compound.setAnd(and);
+      MatcherSet matcher1 = marshalFilter(firstMatcher,
+              Collections.emptyIterator(), and);
+      compound.addMatcherSet(matcher1);
+      FeatureMatcherI nextMatcher = filters.next();
+      MatcherSet matcher2 = marshalFilter(nextMatcher, filters, and);
+      compound.addMatcherSet(matcher2);
+      result.setCompoundMatcher(compound);
+    }
+    else
+    {
+      /*
+       * single condition matcher
+       */
+      MatchCondition matcherModel = new MatchCondition();
+      matcherModel.setCondition(
+              firstMatcher.getMatcher().getCondition().getStableName());
+      matcherModel.setValue(firstMatcher.getMatcher().getPattern());
+      if (firstMatcher.isByAttribute())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYATTRIBUTE);
+        matcherModel.setAttributeName(firstMatcher.getAttribute());
+      }
+      else if (firstMatcher.isByLabel())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYLABEL);
+      }
+      else if (firstMatcher.isByScore())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYSCORE);
+      }
+      result.setMatchCondition(matcherModel);
+    }
+  
+    return result;
+  }
+
+  /**
+   * Loads one XML model of a feature filter to a Jalview object
+   * 
+   * @param featureType
+   * @param matcherSetModel
+   * @return
+   */
+  protected static FeatureMatcherSetI unmarshalFilter(
+          String featureType, MatcherSet matcherSetModel)
+  {
+    FeatureMatcherSetI result = new FeatureMatcherSet();
+    try
+    {
+      unmarshalFilterConditions(result, matcherSetModel, true);
+    } catch (IllegalStateException e)
+    {
+      // mixing AND and OR conditions perhaps
+      System.err.println(
+              String.format("Error reading filter conditions for '%s': %s",
+                      featureType, e.getMessage()));
+      // return as much as was parsed up to the error
+    }
+  
+    return result;
+  }
+
+  /**
+   * Adds feature match conditions to matcherSet as unmarshalled from XML
+   * (possibly recursively for compound conditions)
+   * 
+   * @param matcherSet
+   * @param matcherSetModel
+   * @param and
+   *          if true, multiple conditions are AND-ed, else they are OR-ed
+   * @throws IllegalStateException
+   *           if AND and OR conditions are mixed
+   */
+  protected static void unmarshalFilterConditions(
+          FeatureMatcherSetI matcherSet, MatcherSet matcherSetModel,
+          boolean and)
+  {
+    MatchCondition mc = matcherSetModel.getMatchCondition();
+    if (mc != null)
+    {
+      /*
+       * single condition
+       */
+      FeatureMatcherByType filterBy = mc.getBy();
+      Condition cond = Condition.fromString(mc.getCondition());
+      String pattern = mc.getValue();
+      FeatureMatcherI matchCondition = null;
+      if (filterBy == FeatureMatcherByType.BYLABEL)
+      {
+        matchCondition = FeatureMatcher.byLabel(cond, pattern);
+      }
+      else if (filterBy == FeatureMatcherByType.BYSCORE)
+      {
+        matchCondition = FeatureMatcher.byScore(cond, pattern);
+  
+      }
+      else if (filterBy == FeatureMatcherByType.BYATTRIBUTE)
+      {
+        String[] attNames = mc.getAttributeName();
+        matchCondition = FeatureMatcher.byAttribute(cond, pattern,
+                attNames);
+      }
+  
+      /*
+       * note this throws IllegalStateException if AND-ing to a 
+       * previously OR-ed compound condition, or vice versa
+       */
+      if (and)
+      {
+        matcherSet.and(matchCondition);
+      }
+      else
+      {
+        matcherSet.or(matchCondition);
+      }
+    }
+    else
+    {
+      /*
+       * compound condition
+       */
+      MatcherSet[] matchers = matcherSetModel.getCompoundMatcher()
+              .getMatcherSet();
+      boolean anded = matcherSetModel.getCompoundMatcher().getAnd();
+      if (matchers.length == 2)
+      {
+        unmarshalFilterConditions(matcherSet, matchers[0], anded);
+        unmarshalFilterConditions(matcherSet, matchers[1], anded);
+      }
+      else
+      {
+        System.err.println("Malformed compound filter condition");
+      }
+    }
+  }
+
+  /**
+   * Loads one XML model of a feature colour to a Jalview object
+   * 
+   * @param colourModel
+   * @return
+   */
+  protected static FeatureColourI unmarshalColour(
+          jalview.schemabinding.version2.Colour colourModel)
+  {
+    FeatureColourI colour = null;
+  
+    if (colourModel.hasMax())
+    {
+      Color mincol = null;
+      Color maxcol = null;
+      Color noValueColour = null;
+  
+      try
+      {
+        mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16));
+        maxcol = new Color(Integer.parseInt(colourModel.getRGB(), 16));
+      } catch (Exception e)
+      {
+        Cache.log.warn("Couldn't parse out graduated feature color.", e);
+      }
+  
+      NoValueColour noCol = colourModel.getNoValueColour();
+      if (noCol == NoValueColour.MIN)
+      {
+        noValueColour = mincol;
+      }
+      else if (noCol == NoValueColour.MAX)
+      {
+        noValueColour = maxcol;
+      }
+  
+      colour = new FeatureColour(mincol, maxcol, noValueColour,
+              colourModel.getMin(),
+              colourModel.getMax());
+      String[] attributes = colourModel.getAttributeName();
+      if (attributes != null && attributes.length > 0)
+      {
+        colour.setAttributeName(attributes);
+      }
+      if (colourModel.hasAutoScale())
+      {
+        colour.setAutoScaled(colourModel.getAutoScale());
+      }
+      if (colourModel.hasColourByLabel())
+      {
+        colour.setColourByLabel(colourModel.getColourByLabel());
+      }
+      if (colourModel.hasThreshold())
+      {
+        colour.setThreshold(colourModel.getThreshold());
+      }
+      ColourThreshTypeType ttyp = colourModel.getThreshType();
+      if (ttyp != null)
+      {
+        if (ttyp == ColourThreshTypeType.ABOVE)
+        {
+          colour.setAboveThreshold(true);
+        }
+        else if (ttyp == ColourThreshTypeType.BELOW)
+        {
+          colour.setBelowThreshold(true);
+        }
+      }
+    }
+    else
+    {
+      Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16));
+      colour = new FeatureColour(color);
+    }
+  
+    return colour;
+  }
 }