JAL-2446 merged to spike branch
[jalview.git] / src / jalview / io / StockholmFile.java
index 3be78f4..936d2b9 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,6 +33,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 
@@ -45,7 +46,6 @@ import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 import java.util.Vector;
 
 import com.stevesoft.pat.Regex;
@@ -74,12 +74,14 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class StockholmFile extends AlignFile
 {
+  private static final String ANNOTATION = "annotation";
+
   private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
 
   private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
 
-  private static final Regex DETECT_BRACKETS = new Regex(
-          "(<|>|\\[|\\]|\\(|\\))");
+  public static final Regex DETECT_BRACKETS = new Regex(
+          "(<|>|\\[|\\]|\\(|\\)|\\{|\\})");
 
   StringBuffer out; // output buffer
 
@@ -97,7 +99,8 @@ public class StockholmFile extends AlignFile
     this.al = al;
   }
 
-  public StockholmFile(String inFile, String type) throws IOException
+  public StockholmFile(String inFile, DataSourceType type)
+          throws IOException
   {
     super(inFile, type);
   }
@@ -365,6 +368,11 @@ public class StockholmFile extends AlignFile
 
               // add alignment annotation for this feature
               String key = type2id(type);
+
+              /*
+               * have we added annotation rows for this type ?
+               */
+              boolean annotsAdded = false;
               if (key != null)
               {
                 if (accAnnotations != null
@@ -373,6 +381,7 @@ public class StockholmFile extends AlignFile
                   Vector vv = (Vector) accAnnotations.get(key);
                   for (int ii = 0; ii < vv.size(); ii++)
                   {
+                    annotsAdded = true;
                     AlignmentAnnotation an = (AlignmentAnnotation) vv
                             .elementAt(ii);
                     seqO.addAlignmentAnnotation(an);
@@ -385,6 +394,11 @@ public class StockholmFile extends AlignFile
               while (j.hasMoreElements())
               {
                 String desc = j.nextElement().toString();
+                if (ANNOTATION.equals(desc) && annotsAdded)
+                {
+                  // don't add features if we already added an annotation row
+                  continue;
+                }
                 String ns = content.get(desc).toString();
                 char[] byChar = ns.toCharArray();
                 for (int k = 0; k < byChar.length; k++)
@@ -400,7 +414,7 @@ public class StockholmFile extends AlignFile
                     int new_pos = posmap[k]; // look up nearest seqeunce
                     // position to this column
                     SequenceFeature feat = new SequenceFeature(type, desc,
-                            new_pos, new_pos, 0f, null);
+                            new_pos, new_pos, null);
 
                     seqO.addSequenceFeature(feat);
                   }
@@ -571,22 +585,11 @@ public class StockholmFile extends AlignFile
           {
             String acc = s.stringMatched(1);
             String type = s.stringMatched(2);
-            String seq = new String(s.stringMatched(3));
-            String description = null;
-            // Check for additional information about the current annotation
-            // We use a simple string tokenizer here for speed
-            StringTokenizer sep = new StringTokenizer(seq, " \t");
-            description = sep.nextToken();
-            if (sep.hasMoreTokens())
-            {
-              seq = sep.nextToken();
-            }
-            else
-            {
-              seq = description;
-              description = new String();
-            }
-            // sequence id with from-to fields
+            String oseq = s.stringMatched(3);
+            /*
+             * copy of annotation field that may be processed into whitespace chunks
+             */
+            String seq = new String(oseq);
 
             Hashtable ann;
             // Get an object with all the annotations for this sequence
@@ -601,8 +604,12 @@ public class StockholmFile extends AlignFile
               ann = new Hashtable();
               seqAnn.put(acc, ann);
             }
+
+            // // start of block for appending annotation lines for wrapped
+            // stokchholm file
             // TODO test structure, call parseAnnotationRow with vector from
             // hashtable for specific sequence
+
             Hashtable features;
             // Get an object with all the content for an annotation
             if (ann.containsKey("features"))
@@ -630,15 +637,18 @@ public class StockholmFile extends AlignFile
               content = new Hashtable();
               features.put(this.id2type(type), content);
             }
-            String ns = (String) content.get(description);
+            String ns = (String) content.get(ANNOTATION);
+
             if (ns == null)
             {
               ns = "";
             }
+            // finally, append the annotation line
             ns += seq;
-            content.put(description, ns);
+            content.put(ANNOTATION, ns);
+            // // end of wrapped annotation block.
+            // // Now a new row is created with the current set of data
 
-            // if(type.equals("SS")){
             Hashtable strucAnn;
             if (seqAnn.containsKey(acc))
             {
@@ -655,7 +665,8 @@ public class StockholmFile extends AlignFile
             {
               alan.visible = false;
             }
-            // annotations.addAll(newStruc);
+            // new annotation overwrites any existing annotation...
+
             strucAnn.put(type, newStruc);
             seqAnn.put(acc, strucAnn);
           }
@@ -799,9 +810,9 @@ public class StockholmFile extends AlignFile
   {
     String convert1, convert2 = null;
 
-    convert1 = OPEN_PAREN.replaceAll(annots);
-    convert2 = CLOSE_PAREN.replaceAll(convert1);
-    annots = convert2;
+    // convert1 = OPEN_PAREN.replaceAll(annots);
+    // convert2 = CLOSE_PAREN.replaceAll(convert1);
+    // annots = convert2;
 
     String type = label;
     if (label.contains("_cons"))
@@ -809,12 +820,16 @@ public class StockholmFile extends AlignFile
       type = (label.indexOf("_cons") == label.length() - 5) ? label
               .substring(0, label.length() - 5) : label;
     }
-    boolean ss = false;
+    boolean ss = false, posterior = false;
     type = id2type(type);
-    if (type.equals("secondary structure"))
+    if (type.equalsIgnoreCase("secondary structure"))
     {
       ss = true;
     }
+    if (type.equalsIgnoreCase("posterior probability"))
+    {
+      posterior = true;
+    }
     // decide on secondary structure or not.
     Annotation[] els = new Annotation[annots.length()];
     for (int i = 0; i < annots.length(); i++)
@@ -830,12 +845,12 @@ public class StockholmFile extends AlignFile
           if (DETECT_BRACKETS.search(pos))
           {
             ann.secondaryStructure = Rna.getRNASecStrucState(pos).charAt(0);
+            ann.displayCharacter = "" + pos.charAt(0);
           }
           else
           {
             ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
                     .charAt(0);
-          }
 
           if (ann.secondaryStructure == pos.charAt(0))
           {
@@ -845,9 +860,29 @@ public class StockholmFile extends AlignFile
           {
             ann.displayCharacter = " " + ann.displayCharacter;
           }
+          }
         }
 
       }
+      if (posterior && !ann.isWhitespace()
+              && !Comparison.isGap(pos.charAt(0)))
+      {
+        float val = 0;
+        // symbol encodes values - 0..*==0..10
+        if (pos.charAt(0) == '*')
+        {
+          val = 10;
+        }
+        else
+        {
+          val = pos.charAt(0) - '0';
+          if (val > 9)
+          {
+            val = 10;
+          }
+        }
+        ann.value = val;
+      }
 
       els[i] = ann;
     }
@@ -880,8 +915,13 @@ public class StockholmFile extends AlignFile
     return annot;
   }
 
-  public String print(SequenceI[] s)
+  @Override
+  public String print(SequenceI[] s, boolean jvSuffix)
   {
+    out = new StringBuffer();
+    out.append("# STOCKHOLM 1.0");
+    out.append(newline);
+
     // find max length of id
     int max = 0;
     int maxid = 0;
@@ -889,7 +929,7 @@ public class StockholmFile extends AlignFile
     Hashtable dataRef = null;
     while ((in < s.length) && (s[in] != null))
     {
-      String tmp = printId(s[in]);
+      String tmp = printId(s[in], jvSuffix);
       if (s[in].getSequence().length > max)
       {
         max = s[in].getSequence().length;
@@ -960,47 +1000,44 @@ public class StockholmFile extends AlignFile
     // output annotations
     while (i < s.length && s[i] != null)
     {
-      if (s[i].getDatasetSequence() != null)
+      AlignmentAnnotation[] alAnot = s[i].getAnnotation();
+      if (alAnot != null)
       {
-        SequenceI ds = s[i].getDatasetSequence();
-        AlignmentAnnotation[] alAnot;
         Annotation[] ann;
-        Annotation annot;
-        alAnot = s[i].getAnnotation();
-        String feature = "";
-        if (alAnot != null)
+        for (int j = 0; j < alAnot.length; j++)
         {
-          for (int j = 0; j < alAnot.length; j++)
+
+          String key = type2id(alAnot[j].label);
+          boolean isrna = alAnot[j].isValidStruc();
+
+          if (isrna)
+          {
+            // hardwire to secondary structure if there is RNA secondary
+            // structure on the annotation
+            key = "SS";
+          }
+          if (key == null)
           {
-            if (ds.getSequenceFeatures() != null)
-            {
-              feature = ds.getSequenceFeatures()[0].type;
-            }
-            // ?bug - feature may still have previous loop value
-            String key = type2id(feature);
 
-            if (key == null)
-            {
-              continue;
-            }
+            continue;
+          }
 
-            // out.append("#=GR ");
-            out.append(new Format("%-" + maxid + "s").form("#=GR "
-                    + printId(s[i]) + " " + key + " "));
-            ann = alAnot[j].annotations;
-            boolean isrna = alAnot[j].isValidStruc();
-            String seq = "";
-            for (int k = 0; k < ann.length; k++)
-            {
-              seq += outputCharacter(key, k, isrna, ann, s[i]);
-            }
-            out.append(seq);
-            out.append(newline);
+          // out.append("#=GR ");
+          out.append(new Format("%-" + maxid + "s").form("#=GR "
+                  + printId(s[i], jvSuffix) + " " + key + " "));
+          ann = alAnot[j].annotations;
+          String seq = "";
+          for (int k = 0; k < ann.length; k++)
+          {
+            seq += outputCharacter(key, k, isrna, ann, s[i]);
           }
+          out.append(seq);
+          out.append(newline);
         }
       }
 
-      out.append(new Format("%-" + maxid + "s").form(printId(s[i]) + " "));
+      out.append(new Format("%-" + maxid + "s")
+              .form(printId(s[i], jvSuffix) + " "));
       out.append(s[i].getSequenceAsString());
       out.append(newline);
       i++;
@@ -1053,6 +1090,10 @@ public class StockholmFile extends AlignFile
         out.append(newline);
       }
     }
+
+    out.append("//");
+    out.append(newline);
+
     return out.toString();
   }
 
@@ -1077,8 +1118,8 @@ public class StockholmFile extends AlignFile
     {
       if (annot == null)
       {
-        // sensible gap character if one is available or make one up
-        return sequenceI == null ? '-' : sequenceI.getCharAt(k);
+        // sensible gap character
+        return ' ';
       }
       else
       {
@@ -1105,13 +1146,12 @@ public class StockholmFile extends AlignFile
     return seq;
   }
 
-  @Override
   public String print()
   {
     out = new StringBuffer();
     out.append("# STOCKHOLM 1.0");
     out.append(newline);
-    print(getSeqsAsArray());
+    print(getSeqsAsArray(), false);
 
     out.append("//");
     out.append(newline);
@@ -1125,10 +1165,10 @@ public class StockholmFile extends AlignFile
     if (typeIds == null)
     {
       typeIds = new Hashtable();
-      typeIds.put("SS", "secondary structure");
-      typeIds.put("SA", "surface accessibility");
+      typeIds.put("SS", "Secondary Structure");
+      typeIds.put("SA", "Surface Accessibility");
       typeIds.put("TM", "transmembrane");
-      typeIds.put("PP", "posterior probability");
+      typeIds.put("PP", "Posterior Probability");
       typeIds.put("LI", "ligand binding");
       typeIds.put("AS", "active site");
       typeIds.put("IN", "intron");
@@ -1139,7 +1179,7 @@ public class StockholmFile extends AlignFile
       typeIds.put("DE", "description");
       typeIds.put("DR", "reference");
       typeIds.put("LO", "look");
-      typeIds.put("RF", "reference positions");
+      typeIds.put("RF", "Reference Positions");
 
     }
   }
@@ -1162,7 +1202,7 @@ public class StockholmFile extends AlignFile
     while (e.hasMoreElements())
     {
       Object ll = e.nextElement();
-      if (typeIds.get(ll).toString().equals(type))
+      if (typeIds.get(ll).toString().equalsIgnoreCase(type))
       {
         key = (String) ll;
         break;