JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / io / StockholmFile.java
index 7ca07c8..27be358 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 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.
  * 
@@ -23,6 +23,7 @@
  */
 package jalview.io;
 
+import jalview.analysis.Rna;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -31,6 +32,7 @@ import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 
@@ -40,7 +42,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -70,8 +74,12 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class StockholmFile extends AlignFile
 {
-  // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
-  protected ArrayList<RNA> result;
+  private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
+
+  private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
+
+  private static final Regex DETECT_BRACKETS = new Regex(
+          "(<|>|\\[|\\]|\\(|\\))");
 
   StringBuffer out; // output buffer
 
@@ -99,6 +107,7 @@ public class StockholmFile extends AlignFile
     super(source);
   }
 
+  @Override
   public void initData()
   {
     super.initData();
@@ -116,7 +125,7 @@ public class StockholmFile extends AlignFile
     fr = new FileReader(inFile);
 
     BufferedReader r = new BufferedReader(fr);
-    result = null;
+    List<RNA> result = null;
     try
     {
       result = RNAFactory.loadSecStrStockholm(r);
@@ -153,9 +162,8 @@ public class StockholmFile extends AlignFile
 
       for (int k = 0; k < rna.length(); k++)
       {
-        ann[k] = new Annotation(annot[k], "",
-                jalview.schemes.ResidueProperties.getRNASecStrucState(
-                        annot[k]).charAt(0), 0f);
+        ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                annot[k]).charAt(0), 0f);
 
       }
       AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.",
@@ -176,6 +184,7 @@ public class StockholmFile extends AlignFile
    * @throws IOException
    *           If there is an error with the input file
    */
+  @Override
   public void parse() throws IOException
   {
     StringBuffer treeString = new StringBuffer();
@@ -185,7 +194,7 @@ public class StockholmFile extends AlignFile
     String version;
     // String id;
     Hashtable seqAnn = new Hashtable(); // Sequence related annotations
-    Hashtable seqs = new Hashtable();
+    LinkedHashMap<String, String> seqs = new LinkedHashMap<String, String>();
     Regex p, r, rend, s, x;
     // Temporary line for processing RNA annotation
     // String RNAannot = "";
@@ -197,7 +206,9 @@ public class StockholmFile extends AlignFile
     r = new Regex("# STOCKHOLM ([\\d\\.]+)");
     if (!r.search(nextLine()))
     {
-      throw new IOException(MessageManager.getString("exception.stockholm_invalid_format"));
+      throw new IOException(
+              MessageManager
+                      .getString("exception.stockholm_invalid_format"));
     }
     else
     {
@@ -257,12 +268,11 @@ public class StockholmFile extends AlignFile
           }
         }
         // logger.debug("Number of sequences: " + this.noSeqs);
-        Enumeration accs = seqs.keys();
-        while (accs.hasMoreElements())
+        for (Map.Entry<String, String> skey : seqs.entrySet())
         {
-          String acc = (String) accs.nextElement();
           // logger.debug("Processing sequence " + acc);
-          String seq = (String) seqs.remove(acc);
+          String acc = skey.getKey();
+          String seq = skey.getValue();
           if (maxLength < seq.length())
           {
             maxLength = seq.length();
@@ -416,9 +426,11 @@ public class StockholmFile extends AlignFile
         if (!x.search(line))
         {
           // logger.error("Could not parse sequence line: " + line);
-          throw new IOException(MessageManager.formatMessage("exception.couldnt_parse_sequence_line", new String[]{line}));
+          throw new IOException(MessageManager.formatMessage(
+                  "exception.couldnt_parse_sequence_line",
+                  new String[] { line }));
         }
-        String ns = (String) seqs.get(x.stringMatched(1));
+        String ns = seqs.get(x.stringMatched(1));
         if (ns == null)
         {
           ns = "";
@@ -528,7 +540,8 @@ public class StockholmFile extends AlignFile
           }
           else
           {
-            throw new IOException(MessageManager.formatMessage("exception.error_parsing_line", new String[]{line}));
+            // throw new IOException("Error parsing " + line);
+            System.err.println(">> missing annotation: " + line);
           }
         }
         else if (annType.equals("GC"))
@@ -657,7 +670,9 @@ public class StockholmFile extends AlignFile
         }
         else
         {
-          throw new IOException(MessageManager.formatMessage("exception.unknown_annotation_detected", new String[]{annType,annContent}));
+          throw new IOException(MessageManager.formatMessage(
+                  "exception.unknown_annotation_detected", new String[] {
+                      annType, annContent }));
         }
       }
     }
@@ -770,9 +785,8 @@ public class StockholmFile extends AlignFile
     {
       for (DBRefEntry d : dbrs)
       {
-        jalview.util.MapList mp = new jalview.util.MapList(new int[]
-        { seqO.getStart(), seqO.getEnd() }, new int[]
-        { st, en }, 1, 1);
+        jalview.util.MapList mp = new jalview.util.MapList(new int[] {
+            seqO.getStart(), seqO.getEnd() }, new int[] { st, en }, 1, 1);
         jalview.datamodel.Mapping mping = new Mapping(mp);
         d.setMap(mping);
       }
@@ -780,19 +794,13 @@ public class StockholmFile extends AlignFile
   }
 
   protected static AlignmentAnnotation parseAnnotationRow(
-          Vector annotation, String label, String annots)
+          Vector<AlignmentAnnotation> annotation, String label,
+          String annots)
   {
     String convert1, convert2 = null;
 
-    // Convert all bracket types to parentheses
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
-
-    // Detect if file is RNA by looking for bracket types
-    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
-
-    convert1 = openparen.replaceAll(annots);
-    convert2 = closeparen.replaceAll(convert1);
+    convert1 = OPEN_PAREN.replaceAll(annots);
+    convert2 = CLOSE_PAREN.replaceAll(convert1);
     annots = convert2;
 
     String type = label;
@@ -817,34 +825,37 @@ public class StockholmFile extends AlignFile
       // be written out
       if (ss)
       {
-        if (detectbrackets.search(pos))
-        {
-          ann.secondaryStructure = jalview.schemes.ResidueProperties
-                  .getRNASecStrucState(pos).charAt(0);
-        }
-        else
+        // if (" .-_".indexOf(pos) == -1)
         {
-          ann.secondaryStructure = jalview.schemes.ResidueProperties
-                  .getDssp3state(pos).charAt(0);
-        }
+          if (DETECT_BRACKETS.search(pos))
+          {
+            ann.secondaryStructure = Rna.getRNASecStrucState(pos).charAt(0);
+          }
+          else
+          {
+            ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
+                    .charAt(0);
+          }
 
-        if (ann.secondaryStructure == pos.charAt(0) || pos.charAt(0) == 'C')
-        {
-          ann.displayCharacter = ""; // null; // " ";
-        }
-        else
-        {
-          ann.displayCharacter = " " + ann.displayCharacter;
+          if (ann.secondaryStructure == pos.charAt(0))
+          {
+            ann.displayCharacter = ""; // null; // " ";
+          }
+          else
+          {
+            ann.displayCharacter = " " + ann.displayCharacter;
+          }
         }
+
       }
 
       els[i] = ann;
     }
     AlignmentAnnotation annot = null;
-    Enumeration e = annotation.elements();
+    Enumeration<AlignmentAnnotation> e = annotation.elements();
     while (e.hasMoreElements())
     {
-      annot = (AlignmentAnnotation) e.nextElement();
+      annot = e.nextElement();
       if (annot.label.equals(type))
       {
         break;
@@ -888,18 +899,18 @@ public class StockholmFile extends AlignFile
       {
         maxid = tmp.length();
       }
-      if (s[in].getDBRef() != null)
+      if (s[in].getDBRefs() != null)
       {
-        for (int idb = 0; idb < s[in].getDBRef().length; idb++)
+        for (int idb = 0; idb < s[in].getDBRefs().length; idb++)
         {
           if (dataRef == null)
           {
             dataRef = new Hashtable();
           }
 
-          String datAs1 = s[in].getDBRef()[idb].getSource().toString()
+          String datAs1 = s[in].getDBRefs()[idb].getSource().toString()
                   + " ; "
-                  + s[in].getDBRef()[idb].getAccessionId().toString();
+                  + s[in].getDBRefs()[idb].getAccessionId().toString();
           dataRef.put(tmp, datAs1);
         }
       }
@@ -965,6 +976,7 @@ public class StockholmFile extends AlignFile
             {
               feature = ds.getSequenceFeatures()[0].type;
             }
+            // ?bug - feature may still have previous loop value
             String key = type2id(feature);
 
             if (key == null)
@@ -976,33 +988,11 @@ public class StockholmFile extends AlignFile
             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++)
             {
-              annot = ann[k];
-              String ch = (annot == null) ? Character.toString(s[i]
-                      .getCharAt(k)) : annot.displayCharacter;
-              if (ch.length() == 0)
-              {
-                if (key.equals("SS"))
-                {
-                  char ll = annot.secondaryStructure;
-                  seq = (Character.toString(ll).equals(" ")) ? seq + "C"
-                          : seq + ll;
-                }
-                else
-                {
-                  seq += ".";
-                }
-              }
-              else if (ch.length() == 1)
-              {
-                seq += ch;
-              }
-              else if (ch.length() > 1)
-              {
-                seq += ch.charAt(1);
-              }
+              seq += outputCharacter(key, k, isrna, ann, s[i]);
             }
             out.append(seq);
             out.append(newline);
@@ -1023,54 +1013,41 @@ public class StockholmFile extends AlignFile
       for (int ia = 0; ia < al.getAlignmentAnnotation().length; ia++)
       {
         aa = al.getAlignmentAnnotation()[ia];
-        if (aa.autoCalculated || !aa.visible)
+        if (aa.autoCalculated || !aa.visible || aa.sequenceRef != null)
         {
           continue;
         }
         String seq = "";
         String label;
-
+        String key = "";
         if (aa.label.equals("seq"))
         {
           label = "seq_cons";
         }
         else
         {
-          label = type2id(aa.label.toLowerCase()) + "_cons";
+          key = type2id(aa.label.toLowerCase());
+          if (key == null)
+          {
+            label = aa.label;
+          }
+          else
+          {
+            label = key + "_cons";
+          }
         }
-
         if (label == null)
         {
           label = aa.label;
         }
+        label = label.replace(" ", "_");
 
         out.append(new Format("%-" + maxid + "s").form("#=GC " + label
                 + " "));
         boolean isrna = aa.isValidStruc();
         for (int j = 0; j < aa.annotations.length; j++)
         {
-          String ch = (aa.annotations[j] == null) ? "-"
-                  : aa.annotations[j].displayCharacter;
-          if (ch.length() == 0 || isrna)
-          {
-            char ll = aa.annotations[j].secondaryStructure;
-            if (Character.toString(ll).equals(" "))
-            {
-              seq += "C";
-            }
-            else
-            {
-              seq += ll;
-            }
-          }
-          else if (ch.length() == 1)
-          {
-            seq += ch;
-          }
-          else if (ch.length() > 1)
-          {
-            seq += ch.charAt(1);
-          }
+          seq += outputCharacter(key, j, isrna, aa.annotations, null);
         }
         out.append(seq);
         out.append(newline);
@@ -1079,6 +1056,56 @@ public class StockholmFile extends AlignFile
     return out.toString();
   }
 
+  /**
+   * add an annotation character to the output row
+   * 
+   * @param seq
+   * @param key
+   * @param k
+   * @param isrna
+   * @param ann
+   * @param sequenceI
+   */
+  private char outputCharacter(String key, int k, boolean isrna,
+          Annotation[] ann, SequenceI sequenceI)
+  {
+    char seq = ' ';
+    Annotation annot = ann[k];
+    String ch = (annot == null) ? ((sequenceI == null) ? "-" : Character
+            .toString(sequenceI.getCharAt(k))) : annot.displayCharacter;
+    if (key != null && key.equals("SS"))
+    {
+      if (annot == null)
+      {
+        // sensible gap character if one is available or make one up
+        return sequenceI == null ? '-' : sequenceI.getCharAt(k);
+      }
+      else
+      {
+        // valid secondary structure AND no alternative label (e.g. ' B')
+        if (annot.secondaryStructure > ' ' && ch.length() < 2)
+        {
+          return annot.secondaryStructure;
+        }
+      }
+    }
+
+    if (ch.length() == 0)
+    {
+      seq = '.';
+    }
+    else if (ch.length() == 1)
+    {
+      seq = ch.charAt(0);
+    }
+    else if (ch.length() > 1)
+    {
+      seq = ch.charAt(1);
+    }
+    return seq;
+  }
+
+  @Override
   public String print()
   {
     out = new StringBuffer();
@@ -1092,6 +1119,7 @@ public class StockholmFile extends AlignFile
   }
 
   private static Hashtable typeIds = null;
+
   static
   {
     if (typeIds == null)