Merge branch 'develop' into features/JAL-4134_use_annotation_row_for_colours_and_groups
authorJames Procter <j.procter@dundee.ac.uk>
Fri, 19 May 2023 17:07:01 +0000 (18:07 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Fri, 19 May 2023 17:07:01 +0000 (18:07 +0100)
 Conflicts:
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/ContactMatrix.java
src/jalview/datamodel/ContactMatrixI.java
src/jalview/datamodel/SeqDistanceContactMatrix.java
src/jalview/datamodel/annotations/AnnotationRowBuilder.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/TreeCanvas.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/ContactMapRenderer.java
src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java
src/jalview/ws/dbsources/EBIAlfaFold.java
src/jalview/xml/binding/jalview/MatrixType.java
test/jalview/analysis/AverageDistanceEngineTest.java
test/jalview/project/Jalview2xmlTests.java

15 files changed:
1  2 
resources/lang/Messages.properties
src/jalview/datamodel/ContactMatrix.java
src/jalview/datamodel/ContactMatrixI.java
src/jalview/datamodel/SeqDistanceContactMatrix.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/TreeCanvas.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/ContactMapRenderer.java
src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java
src/jalview/ws/dbsources/EBIAlfaFold.java
src/jalview/xml/binding/jalview/MatrixType.java
test/jalview/analysis/AverageDistanceEngineTest.java
test/jalview/project/Jalview2xmlTests.java

Simple merge
@@@ -160,21 -172,64 +160,17 @@@ public abstract class ContactMatrix imp
    {
      return "Contact Matrix";
    }
--
 -  List<BitSet> groups = null;
 -
 -  @Override
 -  public void updateGroups(List<BitSet> colGroups)
 -  {
 -    groups = colGroups;
 -    colorMap = new HashMap<>();
 -  }
 -
 -  @Override
 -  public boolean hasGroups()
 -  {
 -    return groups != null && groups.size() > 0;
 -  }
 -
 -  @Override
 -  public List<BitSet> getGroups()
 -  {
 -    return groups;
 -  }
 -
 -  @Override
 -  public BitSet getGroupsFor(int column)
 -  {
 -    for (BitSet gp : groups)
 -    {
 -      if (gp.get(column))
 -      {
 -        return gp;
 -      }
 -    }
 -    return ContactMatrixI.super.getGroupsFor(column);
 -  }
 -
 -  HashMap<BitSet, Color> colorMap = new HashMap<>();
 -
 +  GroupSet grps = new GroupSet();
    @Override
 -  public Color getColourForGroup(BitSet bs)
 +  public GroupSetI getGroupSet()
    {
 -    if (bs == null)
 -    {
 -      return Color.white;
 -    }
 -    Color groupCol = colorMap.get(bs);
 -    if (groupCol == null)
 -    {
 -      return Color.white;
 -    }
 -    return groupCol;
 +    return grps;
    }
--
    @Override
 -  public void setColorForGroup(BitSet bs, Color color)
 +  public void setGroupSet(GroupSet makeGroups)
    {
 -    colorMap.put(bs, color);
 +    grps = makeGroups;
    }
--
    public static String contactToFloatString(ContactMatrixI cm)
    {
      StringBuilder sb = new StringBuilder();
      float[][] vals = new float[cols][rows];
      StringTokenizer tabsep = new StringTokenizer(values, "" + '\t');
      int c = 0, r = 0;
--
      while (tabsep.hasMoreTokens())
      {
        double elem = Double.valueOf(tabsep.nextToken());
        }
        if (c >= vals.length)
        {
--
          break;
        }
      }
        Console.warn(
                "Ignoring additional elements for Float string to contact matrix parsing.");
      }
--
      return vals;
    }
  }
@@@ -27,18 -31,8 +27,16 @@@ public interface ContactMatrix
    String getType();
  
    int getWidth();
--
    int getHeight();
 +  public GroupSetI getGroupSet();
 +
 +  /// proxy methods to simplify use of the interface
 +  /// Mappable contact matrices can override these to perform mapping
 +
 +  default public boolean hasGroupSet()
 +  {
 +    return getGroupSet() != null;
 +  }
  
    default boolean hasGroups()
    {
  
    default Color getColourForGroup(BitSet bs)
    {
 -    return Color.white;
 -  };
 +    if (hasGroupSet())
 +    {
 +      return getGroupSet().getColourForGroup(bs);
 +    }
 +    else
 +    {
 +      return Color.white;
 +    }
 +  }
 +
 +  void setGroupSet(GroupSet makeGroups);
  }
@@@ -5,21 -5,16 +5,19 @@@ import java.util.BitSet
  import java.util.HashMap;
  import java.util.List;
  
 +import jalview.util.MapList;
 +import jalview.ws.datamodel.alphafold.MappableContactMatrix;
  /**
   * Dummy contact matrix based on sequence distance
   * 
   * @author jprocter
   *
   */
 -public class SeqDistanceContactMatrix implements ContactMatrixI
 +public class SeqDistanceContactMatrix
 +        extends MappableContactMatrix<SeqDistanceContactMatrix>
 +        implements ContactMatrixI
  {
    private static final String SEQUENCE_DISTANCE = "SEQUENCE_DISTANCE";
--
    private int width = 0;
  
    public SeqDistanceContactMatrix(int width)
    {
      return width;
    }
--
 -  private List<BitSet> groups = null;
 -
 -  @Override
 -  public void updateGroups(List<BitSet> colGroups)
 -  {
 -    groups = colGroups;
 -  }
 -
    @Override
 -  public boolean hasGroups()
 +  protected double getElementAt(int _column, int i)
    {
 -    return groups != null;
 +    return Math.abs(_column - i);
    }
--
 -  @Override
 -  public List<BitSet> getGroups()
 -  {
 -    return groups;
 -  }
 -
 -  HashMap<BitSet, Color> colorMap = new HashMap<>();
 -
    @Override
 -  public Color getColourForGroup(BitSet bs)
 +  protected SeqDistanceContactMatrix newMappableContactMatrix(
 +          SequenceI newRefSeq, MapList newFromMapList)
    {
 -    if (bs == null)
 -    {
 -      return Color.white;
 -    }
 -    Color groupCol = colorMap.get(bs);
 -    if (groupCol == null)
 -    {
 -      return Color.white;
 -    }
 -    return groupCol;
 -  }
  
 -  @Override
 -  public void setColorForGroup(BitSet bs, Color color)
 -  {
 -    colorMap.put(bs, color);
 +    return new SeqDistanceContactMatrix(width);
    }
  }
@@@ -89,6 -88,11 +88,10 @@@ public class JmolParser extends Structu
      super(inFile, sourceType, tempfacType);
    }
  
+   public JmolParser(FileParse fp, boolean doXferSettings) throws IOException
+   {
+     super(fp, doXferSettings);
+   }
 -
    public JmolParser(FileParse fp) throws IOException
    {
      super(fp);
        // add a PAEMatrix if set (either by above or otherwise)
        if (hasPAEMatrix())
        {
 -        Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
 -        EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
 -                null, false, false, null);
 -
 -        if (al.getAlignmentAnnotation() != null)
 +        try
          {
 -          for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
 +          Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
 +          EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
-                   null, false, false);
++                  null, false, false, null);
 +
 +          if (al.getAlignmentAnnotation() != null)
            {
 -            annotations.add(alann);
 +            for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
 +            {
 +              annotations.add(alann);
 +            }
            }
 +        } catch (Throwable ff)
 +        {
 +          Console.error("Couldn't import PAE Matrix from " + getPAEMatrix(),
 +                  ff);
 +          warningMessage += "Couldn't import PAE Matrix"
 +                  + getNewlineString() + ff.getLocalizedMessage()
 +                  + getNewlineString();
          }
        }
      } catch (OutOfMemoryError er)
      {
        System.out.println(
Simple merge
Simple merge
@@@ -1108,7 -1071,7 +1108,6 @@@ public class TreeCanvas extends JPanel 
        return false;
      }
      ColumnSelection cs = av.getColumnSelection();
--
      HiddenColumns hc = av.getAlignment().getHiddenColumns();
      int offp = (rseq != null) ? rseq.findIndex(rseq.getStart() + colm)
              : colm;
      }
      return false;
    }
--
    private BitSet createColumnGroupFor(Vector<BinaryNode> l, Color col)
    {
      BitSet gp = new BitSet();
        {
          continue;
        }
 +      // TODO - sort indices for faster lookup
        ColumnSelection cs = av.getColumnSelection();
        HiddenColumns hc = av.getAlignment().getHiddenColumns();
 +      ContactMatrixI cm = av.getContactMatrix(tp.assocAnnotation);
 +      MappableContactMatrixI mcm = null;
 +      int offp;
 +      if (cm instanceof MappableContactMatrixI)
 +      {
 +        mcm = (MappableContactMatrixI) cm;
 +        int[] seqpos = mcm.getMappedPositionsFor(
 +                tp.assocAnnotation.sequenceRef, colm);
 +        if (seqpos == null)
 +        {
 +          // no mapping for this column.
 +          continue;
 +        }
 +        // TODO: handle ranges...
 +        offp = seqpos[0];
 +      }
 +      else
        {
 -        int offp = (rseq != null) ? rseq.findIndex(rseq.getStart() + colm)
 +        offp = (rseq != null) ? rseq.findIndex(rseq.getStart() + colm)
                  : colm;
 -
 -        if (!av.hasHiddenColumns() || hc.isVisible(offp - 1))
 +      }
 +      if (!av.hasHiddenColumns() || hc.isVisible(offp - 1))
 +      {
 +        if (cs.contains(offp - 1))
          {
 -          if (cs.contains(offp - 1))
 -          {
 -            cs.removeElement(offp - 1);
 -          }
 -          else
 -          {
 -            cs.addElement(offp - 1);
 -          }
 +          cs.removeElement(offp - 1);
 +        }
 +        else
 +        {
 +          cs.addElement(offp - 1);
          }
        }
      }
@@@ -2339,40 -2337,8 +2339,39 @@@ public class Jalview2XM
                {
                  xmlmat.setCutHeight(cm.getCutHeight());
                }
--
                // set/get properties
 +              if (cm instanceof MappableContactMatrixI)
 +              {
 +                jalview.util.MapList mlst = ((MappableContactMatrixI) cm)
 +                        .getMapFor(annotation.sequenceRef);
 +                if (mlst != null)
 +                {
 +                  MapListType mp = new MapListType();
 +                  List<int[]> r = mlst.getFromRanges();
 +                  for (int[] range : r)
 +                  {
 +                    MapListFrom mfrom = new MapListFrom();
 +                    mfrom.setStart(range[0]);
 +                    mfrom.setEnd(range[1]);
 +                    // mp.addMapListFrom(mfrom);
 +                    mp.getMapListFrom().add(mfrom);
 +                  }
 +                  r = mlst.getToRanges();
 +                  for (int[] range : r)
 +                  {
 +                    MapListTo mto = new MapListTo();
 +                    mto.setStart(range[0]);
 +                    mto.setEnd(range[1]);
 +                    // mp.addMapListTo(mto);
 +                    mp.getMapListTo().add(mto);
 +                  }
 +                  mp.setMapFromUnit(
 +                          BigInteger.valueOf(mlst.getFromRatio()));
 +                  mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
 +                  xmlmat.setMapping(mp);
 +                }
 +              }
 +              // and add to model
                an.getContactmatrix().add(xmlmat);
              }
            }
  
    }
  
 +  private String stringifyBitset(BitSet gp)
 +  {
 +    StringBuilder sb = new StringBuilder();
 +    for (long val : gp.toLongArray())
 +    {
 +      if (sb.length() > 0)
 +      {
 +        sb.append(",");
 +      }
 +      sb.append(val);
 +    }
 +    return sb.toString();
 +  }
 +
 +  private BitSet deStringifyBitset(String stringified)
 +  {
 +    if ("".equals(stringified) || stringified == null)
 +    {
 +      return new BitSet();
 +    }
 +    String[] longvals = stringified.split(",");
 +    long[] newlongvals = new long[longvals.length];
 +    for (int lv = 0; lv < longvals.length; lv++)
 +    {
 +      try
 +      {
 +        newlongvals[lv] = Long.valueOf(longvals[lv]);
 +      } catch (Exception x)
 +      {
 +        errorMessage += "Couldn't destringify bitset from: '" + stringified
 +                + "'";
 +        newlongvals[lv] = 0;
 +      }
 +    }
 +    return BitSet.valueOf(newlongvals);
 +
 +  }
    private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
    {
      AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
                      Console.log.info(
                              "Ignoring additional clusterings for contact matrix");
                    }
--
                    String treeMethod = xmlmat.getTreeMethod();
                    double thresh = xmlmat.getCutHeight() != null
                            ? xmlmat.getCutHeight()
@@@ -35,12 -35,12 +35,10 @@@ public abstract class ContactMapRendere
       * shown when no data available from map
       */
      Color no_data;
--
      /**
       * shown for region not currently visible - should normally not see this
       */
      Color hidden;
--
      /**
       * linear shading scheme min/max
       */
          x++;
          continue;
        }
 +      // ContactListI from viewport can map column -> group
        Color gpcol = (cm == null) ? Color.white
 -              : cm.getColourForGroup(cm.getGroupsFor(column));
 +              : contacts.getColourForGroup(); // cm.getColourForGroup(cm.getGroupsFor(column));
        // feature still in development - highlight or omit regions hidden in
        // the alignment - currently marks them as red rows
        boolean maskHiddenCols = false;
                    (int) (((float) (col.getBlue() + gpcol.getBlue())) / 2f));
          }
          g.setColor(col);
--
          if (cgeom.pixels_step > 1)
          {
            g.fillRect(x * charWidth, ht, charWidth, 1 + cgeom.pixels_step);
@@@ -23,30 -15,39 +23,34 @@@ import jalview.datamodel.ContactListI
  import jalview.datamodel.ContactListImpl;
  import jalview.datamodel.ContactListProviderI;
  import jalview.datamodel.ContactMatrixI;
 +import jalview.datamodel.GroupSet;
 +import jalview.datamodel.GroupSetI;
 +import jalview.datamodel.Mapping;
 +import jalview.datamodel.SequenceDummy;
  import jalview.datamodel.SequenceI;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormatException;
 +import jalview.io.FileParse;
 +import jalview.util.MapList;
  import jalview.util.MapUtils;
 +import jalview.ws.dbsources.EBIAlfaFold;
  
 -public class PAEContactMatrix implements ContactMatrixI
 +public class PAEContactMatrix extends
 +        MappableContactMatrix<PAEContactMatrix> implements ContactMatrixI
  {
 -  SequenceI refSeq = null;
 -
 -  /**
 -   * the length that refSeq is expected to be (excluding gaps, of course)
 -   */
 -  int length;
    int maxrow = 0, maxcol = 0;
  
 -  int[] indices1, indices2;
    float[][] elements;
  
    float maxscore;
  
 -  private void setRefSeq(SequenceI _refSeq)
 -  {
 -    refSeq = _refSeq;
 -    while (refSeq.getDatasetSequence() != null)
 -    {
 -      refSeq = refSeq.getDatasetSequence();
 -    }
 -    length = _refSeq.getEnd() - _refSeq.getStart() + 1;
 -  }
    @SuppressWarnings("unchecked")
    public PAEContactMatrix(SequenceI _refSeq, Map<String, Object> pae_obj)
 +          throws FileFormatException
    {
      setRefSeq(_refSeq);
      // convert the lists to primitive arrays and store
        while (scores.hasNext())
        {
          Object d = scores.next();
          if (d instanceof Double)
 +        {
            elements[row][col++] = ((Double) d).longValue();
 +        }
          else
 +        {
            elements[row][col++] = (float) ((Long) d).longValue();
 +        }
 +
 +        if (maxscore < elements[row][col - 1])
 +        {
 +          maxscore = elements[row][col - 1];
 +        }
        }
        row++;
        col = 0;
    }
  
    @Override
 -  public ContactListI getContactList(final int _column)
 +  public ContactListI getContactList(final int column)
    {
-     // final int _column;
-     // if (toSeq != null)
-     // {
-     // int[] word = toSeq.locateInTo(column, column);
-     // if (word == null)
-     // {
-     // return null;
-     // }
-     // _column = word[0];
-     // }
-     // else
-     // {
-     // _column = column;
-     // }
 -    if (_column < 0 || _column >= elements.length)
 +    if (column < 0 || column >= elements.length)
      {
        return null;
      }
    {
      return length;
    }
--
 -  List<BitSet> groups = null;
 -
 -  @Override
 -  public boolean hasGroups()
 -  {
 -    return groups != null;
 -  }
 -
 -  String newick = null;
 -
 -  @Override
 -  public String getNewick()
 -  {
 -    return newick;
 -  }
 -
 -  @Override
 -  public boolean hasTree()
 -  {
 -    return newick != null && newick.length() > 0;
 -  }
 -
 -  boolean abs;
 -
 -  double thresh;
 -
 -  String treeType = null;
 -
 -  public void makeGroups(float thresh, boolean abs)
 +  public static void validateContactMatrixFile(String fileName)
 +          throws FileFormatException, IOException
    {
 -    AverageDistanceEngine clusterer = new AverageDistanceEngine(null, null,
 -            this);
 -    double height = clusterer.findHeight(clusterer.getTopNode());
 -    newick = new jalview.io.NewickFile(clusterer.getTopNode(), false, true)
 -            .print();
 -    treeType = "UPGMA";
 -    Console.trace("Newick string\n" + newick);
 -
 -    List<BinaryNode> nodegroups;
 -    if (abs ? height > thresh : 0 < thresh && thresh < 1)
 +    FileInputStream infile = null;
 +    try
      {
 -      float cut = abs ? (float) (thresh / height) : thresh;
 -      Console.debug("Threshold " + cut + " for height=" + height);
 -
 -      nodegroups = clusterer.groupNodes(cut);
 -    }
 -    else
 +      infile = new FileInputStream(new File(fileName));
 +    } catch (Throwable t)
      {
 -      nodegroups = new ArrayList<BinaryNode>();
 -      nodegroups.add(clusterer.getTopNode());
 +      new IOException("Couldn't open " + fileName, t);
      }
 -    this.abs = abs;
 -    this.thresh = thresh;
 -    groups = new ArrayList<>();
 -    for (BinaryNode root : nodegroups)
 +    JSONObject paeDict = null;
 +    try
      {
 -      BitSet gpset = new BitSet();
 -      for (BinaryNode leaf : clusterer.findLeaves(root))
 -      {
 -        gpset.set((Integer) leaf.element());
 -      }
 -      groups.add(gpset);
 -    }
 -  }
 -
 -  @Override
 -  public void updateGroups(List<BitSet> colGroups)
 -  {
 -    if (colGroups != null)
 +      paeDict = EBIAlfaFold.parseJSONtoPAEContactMatrix(infile);
 +    } catch (Throwable t)
      {
 -      groups = colGroups;
 +      new FileFormatException("Couldn't parse " + fileName
 +              + " as a JSON dict or array containing a dict");
      }
 -  }
  
 -  @Override
 -  public BitSet getGroupsFor(int column)
 -  {
 -    for (BitSet gp : groups)
 +    PAEContactMatrix matrix = new PAEContactMatrix(
 +            new SequenceDummy("Predicted"), (Map<String, Object>) paeDict);
 +    if (matrix.getWidth() <= 0)
      {
 -      if (gp.get(column))
 -      {
 -        return gp;
 -      }
 -    }
 -    return ContactMatrixI.super.getGroupsFor(column);
 -  }
 -
 -  HashMap<BitSet, Color> colorMap = new HashMap<>();
 -
 -  @Override
 -  public Color getColourForGroup(BitSet bs)
 -  {
 -    if (bs == null)
 -    {
 -      return Color.white;
 -    }
 -    Color groupCol = colorMap.get(bs);
 -    if (groupCol == null)
 -    {
 -      return Color.white;
 +      throw new FileFormatException(
 +              "No data in PAE matrix read from '" + fileName + "'");
      }
 -    return groupCol;
 -  }
 -
 -  @Override
 -  public void setColorForGroup(BitSet bs, Color color)
 -  {
 -    colorMap.put(bs, color);
    }
--
 -  public void restoreGroups(List<BitSet> newgroups, String treeMethod,
 -          String tree, double thresh2)
 -  {
 -    treeType = treeMethod;
 -    groups = newgroups;
 -    thresh = thresh2;
 -    newick = tree;
 -
 -  }
 -
 -  @Override
 -  public boolean hasCutHeight()
 -  {
 -    return groups != null && thresh != 0;
 -  }
 -
 -  @Override
 -  public double getCutHeight()
 -  {
 -    return thresh;
 -  }
 -
    @Override
 -  public String getTreeMethod()
 +  protected PAEContactMatrix newMappableContactMatrix(SequenceI newRefSeq,
 +          MapList newFromMapList)
    {
 -    return treeType;
 +    PAEContactMatrix pae = new PAEContactMatrix(newRefSeq, newFromMapList,
 +            elements, new GroupSet(grps));
 +    return pae;
    }
  }
@@@ -443,9 -447,11 +448,11 @@@ public class EBIAlfaFold extends EbiFil
      }
      ContactMatrixI matrix = new PAEContactMatrix(sequence,
              (Map<String, Object>) paeDict);
 -    ((PAEContactMatrix) matrix).makeGroups(5f, true);
 +    matrix.setGroupSet(GroupSet.makeGroups(matrix, 5f, true));
  
      AlignmentAnnotation cmannot = sequence.addContactList(matrix);
+     if (label != null)
+       cmannot.label = label;
      pdbAlignment.addAnnotation(cmannot);
  
      return true;
        return false;
      }
  
-     ContactMatrixI matrix = new PAEContactMatrix(sm.getSequence(),
+     SequenceI seq = sm.getSequence();
+     ContactMatrixI matrix = new PAEContactMatrix(seq,
              (Map<String, Object>) pae_obj);
 -    ((PAEContactMatrix) matrix).makeGroups(5f, true);
 -    AlignmentAnnotation cmannot = seq.addContactList(matrix);
 +    matrix.setGroupSet(GroupSet.makeGroups(matrix, 5f, true));
 +    AlignmentAnnotation cmannot = sm.getSequence().addContactList(matrix);
-     sm.getSequence().addAlignmentAnnotation(cmannot);
+     /* this already happens in Sequence.addContactList()
+      seq.addAlignmentAnnotation(cmannot);
+      */
      return true;
    }
  
@@@ -2,9 -2,9 +2,10 @@@
  // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
  // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
  // Any modifications to this file will be lost upon recompilation of the source schema. 
 -// Generated on: 2023.03.17 at 05:31:44 PM GMT 
 +// Generated on: 2023.05.13 at 06:58:41 PM BST 
  //
  
++
  package jalview.xml.binding.jalview;
  
  import java.math.BigInteger;
@@@ -16,6 -16,6 +17,7 @@@ import javax.xml.bind.annotation.XmlAtt
  import javax.xml.bind.annotation.XmlElement;
  import javax.xml.bind.annotation.XmlType;
  
++
  /**
   * <p>
   * Java class for MatrixType complex type.
@@@ -103,7 -103,7 +103,6 @@@ public class AverageDistanceEngineTes
        }
        System.out.println("\\");
      }
--
    }
  
  }
@@@ -1575,34 -1573,6 +1577,33 @@@ public class Jalview2xmlTests extends J
      Assert.assertNotSame(dummyMat.getNewick(), "");
      AlignmentAnnotation paeCm = sq.addContactList(dummyMat);
      al.addAnnotation(paeCm);
 +    // verify store/restore of group bitsets
 +    for (BitSet gp : dummyMat.getGroups())
 +    {
 +      StringBuilder sb = new StringBuilder();
 +      for (long val : gp.toLongArray())
 +      {
 +        if (sb.length() > 0)
 +        {
 +          sb.append(",");
 +        }
 +        sb.append(val);
 +      }
 +      String[] longvals = sb.toString().split(",");
 +      long[] newlongvals = new long[longvals.length];
 +      for (int lv = 0; lv < longvals.length; lv++)
 +      {
 +        try
 +        {
 +          newlongvals[lv] = Long.valueOf(longvals[lv]);
 +        } catch (Exception x)
 +        {
 +          Assert.fail("failed to deserialise bitset element ");
 +        }
 +      }
 +      BitSet newGp = BitSet.valueOf(newlongvals);
 +      assertTrue(gp.equals(newGp));
 +    }
      File tfile = File.createTempFile("testStoreAndRecoverPAEmatrix",
              ".jvp");
      new Jalview2XML(false).saveState(tfile);
      ContactMatrixI restoredMat = newSeq
              .getContactMatrixFor(newSeq.getAnnotation()[0]);
      Assert.assertNotNull(restoredMat);
 +    MapList oldMap = ((MappableContactMatrixI) dummyMat).getMapFor(sq);
 +    MapList newMap = ((MappableContactMatrixI) restoredMat)
 +            .getMapFor(newSeq);
 +    Assert.assertEquals(oldMap.getFromRanges(), newMap.getFromRanges());
 +    Assert.assertEquals(oldMap.getToRanges(), newMap.getToRanges());
 +    Assert.assertEquals(oldMap.getFromRatio(), newMap.getFromRatio());
 +    Assert.assertEquals(oldMap.getToRatio(), newMap.getToRatio());
      for (i = sq.getLength() - 1; i >= 0; i--)
      {
        ContactListI oldCM = dummyMat.getContactList(i),
      Assert.assertEquals(restoredMat.getGroups(), dummyMat.getGroups());
      Assert.assertEquals(restoredMat.hasTree(), dummyMat.hasTree());
      Assert.assertEquals(restoredMat.getNewick(), dummyMat.getNewick());
--
    }
  
  }