exploring making PAE storage in projects more efficient
[jalview.git] / src / jalview / project / Jalview2XML.java
index 8142435..b9e1f46 100644 (file)
@@ -26,6 +26,7 @@ import static jalview.math.RotatableMatrix.Axis.Z;
 
 import java.awt.Color;
 import java.awt.Font;
+import java.awt.FontMetrics;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
@@ -44,6 +45,7 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.GregorianCalendar;
@@ -94,6 +96,7 @@ import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GeneLocus;
 import jalview.datamodel.GraphLine;
+import jalview.datamodel.GroupSet;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Point;
 import jalview.datamodel.RnaViewerModel;
@@ -150,6 +153,7 @@ import jalview.viewmodel.ViewportRanges;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
+import jalview.ws.datamodel.MappableContactMatrixI;
 import jalview.ws.datamodel.alphafold.PAEContactMatrix;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.dm.AAConSettings;
@@ -192,6 +196,7 @@ import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns;
 import jalview.xml.binding.jalview.JalviewModel.Viewport.Overview;
 import jalview.xml.binding.jalview.JalviewUserColours;
 import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.MapListType;
 import jalview.xml.binding.jalview.MapListType.MapListFrom;
 import jalview.xml.binding.jalview.MapListType.MapListTo;
 import jalview.xml.binding.jalview.Mapping;
@@ -278,6 +283,12 @@ public class Jalview2XML
    * entry names
    */
   private Map<RnaModel, String> rnaSessions = new HashMap<>();
+  
+  /**
+   * map from contact matrices to their XML ids
+   */
+  private Map<ContactMatrixI,String> contactMatrices = new HashMap<>();
+  private Map<String, ContactMatrixI> contactMatrixRefs = new HashMap<>();
 
   /**
    * A helper method for safely using the value of an optional attribute that
@@ -1323,6 +1334,13 @@ public class Jalview2XML
               tree.setLinkToAllViews(
                       tp.getTreeCanvas().isApplyToAllViews());
 
+              // columnWiseTree
+              if (tp.isColumnWise())
+              {
+                tree.setColumnWise(true);
+                String annId = tp.getAssocAnnotation().annotationId;
+                tree.setColumnReference(annId);
+              }
               // jms.addTree(tree);
               object.getTree().add(tree);
             }
@@ -1514,7 +1532,7 @@ public class Jalview2XML
                 ov.getCanvas().getResidueColour().getRGB());
         overview.setHiddenColour(ov.getCanvas().getHiddenColour().getRGB());
         view.setOverview(overview);
-      } 
+      }
       if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
       {
         view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
@@ -2295,19 +2313,15 @@ public class Jalview2XML
           line.setColour(annotation.getThreshold().colour.getRGB());
           an.setThresholdLine(line);
         }
-        if (annotation.graph==AlignmentAnnotation.CONTACT_MAP)
+        if (annotation.graph == AlignmentAnnotation.CONTACT_MAP)
         {
-          if (annotation.sequenceRef.getContactMaps()!=null)
+          if (annotation.sequenceRef.getContactMaps() != null)
           {
-            ContactMatrixI cm = annotation.sequenceRef.getContactMatrixFor(annotation);
-            if (cm!=null)
+            ContactMatrixI cm = annotation.sequenceRef
+                    .getContactMatrixFor(annotation);
+            if (cm != null)
             {
-              MatrixType xmlmat = new MatrixType();
-              xmlmat.setType(cm.getType());
-              xmlmat.setRows(BigInteger.valueOf(cm.getWidth()));
-              xmlmat.setCols(BigInteger.valueOf(cm.getHeight()));
-              xmlmat.setValue(ContactMatrix.contactToFloatString(cm));
-              an.getContactmatrix().add(xmlmat);
+              storeMatrixFor(vamsasSet, an,annotation, cm);
             }
           }
         }
@@ -2341,10 +2355,9 @@ public class Jalview2XML
       {
         for (String pr : annotation.getProperties())
         {
-          jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property();
+          jalview.xml.binding.jalview.Property prop = new jalview.xml.binding.jalview.Property();
           prop.setName(pr);
           prop.setValue(annotation.getProperty(pr));
-          // an.addProperty(prop);
           an.getProperty().add(prop);
         }
       }
@@ -2415,6 +2428,115 @@ public class Jalview2XML
 
   }
 
+  private void storeMatrixFor(SequenceSet root, Annotation an, AlignmentAnnotation annotation, ContactMatrixI cm)
+  {
+    String cmId = contactMatrices.get(cm);
+    MatrixType xmlmat=null;
+    if (cmId==null)
+    {
+xmlmat = new MatrixType();
+    xmlmat.setType(cm.getType());
+    xmlmat.setRows(BigInteger.valueOf(cm.getWidth()));
+    xmlmat.setCols(BigInteger.valueOf(cm.getHeight()));
+    // consider using an opaque to/from -> allow instance to control
+    // its representation ?
+    xmlmat.setElements(ContactMatrix.contactToFloatString(cm));
+    if (cm.hasGroups())
+    {
+      for (BitSet gp : cm.getGroups())
+      {
+        xmlmat.getGroups().add(stringifyBitset(gp));
+      }
+    }
+    if (cm.hasTree())
+    {
+      // provenance object for tree ?
+      xmlmat.getNewick().add(cm.getNewick());
+      xmlmat.setTreeMethod(cm.getTreeMethod());
+    }
+    if (cm.hasCutHeight())
+    {
+      xmlmat.setCutHeight(cm.getCutHeight());
+    }
+      xmlmat.setId(makeHashCode(cm, cm.get));
+      root.getMatrices().add(xmlmat);
+    }
+    else {
+      
+    }
+    // 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);
@@ -3919,7 +4041,8 @@ public class Jalview2XML
         jaa.setCalcId(annotation.getCalcId());
         if (annotation.getProperty().size() > 0)
         {
-          for (Annotation.Property prop : annotation.getProperty())
+          for (jalview.xml.binding.jalview.Property prop : annotation
+                  .getProperty())
           {
             jaa.setProperty(prop.getName(), prop.getValue());
           }
@@ -3940,12 +4063,60 @@ public class Jalview2XML
                 else
                 {
                   float[][] elements = ContactMatrix
-                          .fromFloatStringToContacts(xmlmat.getValue(),
+                          .fromFloatStringToContacts(xmlmat.getElements(),
                                   xmlmat.getCols().intValue(),
                                   xmlmat.getRows().intValue());
-
+                  jalview.util.MapList mapping = null;
+                  if (xmlmat.getMapping() != null)
+                  {
+                    MapListType m = xmlmat.getMapping();
+                    // Mapping m = dr.getMapping();
+                    int fr[] = new int[m.getMapListFrom().size() * 2];
+                    Iterator<MapListFrom> from = m.getMapListFrom()
+                            .iterator();// enumerateMapListFrom();
+                    for (int _i = 0; from.hasNext(); _i += 2)
+                    {
+                      MapListFrom mf = from.next();
+                      fr[_i] = mf.getStart();
+                      fr[_i + 1] = mf.getEnd();
+                    }
+                    int fto[] = new int[m.getMapListTo().size() * 2];
+                    Iterator<MapListTo> to = m.getMapListTo().iterator();// enumerateMapListTo();
+                    for (int _i = 0; to.hasNext(); _i += 2)
+                    {
+                      MapListTo mf = to.next();
+                      fto[_i] = mf.getStart();
+                      fto[_i + 1] = mf.getEnd();
+                    }
+
+                    mapping = new jalview.util.MapList(fr, fto,
+                            m.getMapFromUnit().intValue(),
+                            m.getMapToUnit().intValue());
+                  }
+                  List<BitSet> newgroups = new ArrayList<BitSet>();
+                  if (xmlmat.getGroups().size() > 0)
+                  {
+                    for (String sgroup : xmlmat.getGroups())
+                    {
+                      newgroups.add(deStringifyBitset(sgroup));
+                    }
+                  }
+                  String nwk = xmlmat.getNewick().size() > 0
+                          ? xmlmat.getNewick().get(0)
+                          : null;
+                  if (xmlmat.getNewick().size() > 1)
+                  {
+                    Console.log.info(
+                            "Ignoring additional clusterings for contact matrix");
+                  }
+                  String treeMethod = xmlmat.getTreeMethod();
+                  double thresh = xmlmat.getCutHeight() != null
+                          ? xmlmat.getCutHeight()
+                          : 0;
+                  GroupSet grpset = new GroupSet();
+                  grpset.restoreGroups(newgroups, treeMethod, nwk, thresh);
                   PAEContactMatrix newpae = new PAEContactMatrix(
-                          jaa.sequenceRef, elements);
+                          jaa.sequenceRef, mapping, elements, grpset);
                   jaa.sequenceRef.addContactListFor(jaa, newpae);
                 }
               }
@@ -4193,8 +4364,8 @@ public class Jalview2XML
    */
   protected void loadOverview(Viewport view, String version, AlignFrame af)
   {
-    if (!isVersionStringLaterThan("2.11.3",
-            version) && view.getOverview()==null)
+    if (!isVersionStringLaterThan("2.11.3", version)
+            && view.getOverview() == null)
     {
       return;
     }
@@ -4339,10 +4510,28 @@ public class Jalview2XML
         TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
         if (tp == null)
         {
-          tp = af.showNewickTree(new NewickFile(tree.getNewick()),
-                  tree.getTitle(), safeInt(tree.getWidth()),
-                  safeInt(tree.getHeight()), safeInt(tree.getXpos()),
-                  safeInt(tree.getYpos()));
+          if (tree.isColumnWise())
+          {
+            AlignmentAnnotation aa = (AlignmentAnnotation) annotationIds
+                    .get(tree.getColumnReference());
+            if (aa == null)
+            {
+              Console.warn(
+                      "Null alignment annotation when restoring columnwise tree");
+            }
+            tp = af.showColumnWiseTree(new NewickFile(tree.getNewick()), aa,
+                    tree.getTitle(), safeInt(tree.getWidth()),
+                    safeInt(tree.getHeight()), safeInt(tree.getXpos()),
+                    safeInt(tree.getYpos()));
+
+          }
+          else
+          {
+            tp = af.showNewickTree(new NewickFile(tree.getNewick()),
+                    tree.getTitle(), safeInt(tree.getWidth()),
+                    safeInt(tree.getHeight()), safeInt(tree.getXpos()),
+                    safeInt(tree.getYpos()));
+          }
           if (tree.getId() != null)
           {
             // perhaps bind the tree id to something ?
@@ -4882,14 +5071,15 @@ public class Jalview2XML
     viewport.setIncrement(safeInt(view.getConsThreshold()));
     viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
     viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
-    if (view.getCharWidth()!=null)
+    viewport.setFont(
+            new Font(view.getFontName(), safeInt(view.getFontStyle()),
+                    safeInt(view.getFontSize())),
+            (view.getCharWidth() != null) ? false : true);
+    if (view.getCharWidth() != null)
     {
       viewport.setCharWidth(view.getCharWidth());
       viewport.setCharHeight(view.getCharHeight());
     }
-    viewport.setFont(new Font(view.getFontName(),
-            safeInt(view.getFontStyle()), safeInt(view.getFontSize())),
-            true);
     ViewStyleI vs = viewport.getViewStyle();
     vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna());
     viewport.setViewStyle(vs);
@@ -5212,7 +5402,7 @@ public class Jalview2XML
               + annotationId);
       return null;
     }
-    // belt-and-braces create a threshold line if the 
+    // belt-and-braces create a threshold line if the
     // colourscheme needs one but the matchedAnnotation doesn't have one
     if (safeInt(viewAnnColour.getAboveThreshold()) != 0
             && matchedAnnotation.getThreshold() == null)