update author list in license for (JAL-826)
[jalview.git] / src / jalview / io / StockholmFile.java
index d0d84f5..3a7a739 100644 (file)
@@ -1,20 +1,19 @@
 /*\r
- * Jalview - A Sequence Alignment Editor and Viewer\r
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
- *\r
- * This program is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License\r
- * as published by the Free Software Foundation; either version 2\r
- * of the License, or (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
+ * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle\r
+ * \r
+ * This file is part of Jalview.\r
+ * \r
+ * Jalview is free software: you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License \r
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
+ * \r
+ * Jalview is distributed in the hope that it will be useful, but \r
+ * WITHOUT ANY WARRANTY; without even the implied warranty \r
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
+ * PURPOSE.  See the GNU General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
  */\r
 /*\r
  * This extension was written by Benjamin Schuster-Boeckler at sanger.ac.uk\r
@@ -26,14 +25,21 @@ import java.util.*;
 \r
 import com.stevesoft.pat.*;\r
 import jalview.datamodel.*;\r
+import jalview.analysis.Rna;\r
 \r
 // import org.apache.log4j.*;\r
 \r
 /**\r
- * This class is supposed to parse a Stockholm format file into Jalview\r
+ * This class is supposed to parse a Stockholm format file into Jalview There\r
+ * are TODOs in this class: we do not know what the database source and version\r
+ * is for the file when parsing the #GS= AC tag which associates accessions with\r
+ * sequences. Database references are also not parsed correctly: a separate\r
+ * reference string parser must be added to parse the database reference form\r
+ * into Jalview's local representation.\r
  * \r
  * @author bsb at sanger.ac.uk\r
- * @version 0.3\r
+ * @version 0.3 + jalview mods\r
+ * \r
  */\r
 public class StockholmFile extends AlignFile\r
 {\r
@@ -48,6 +54,11 @@ public class StockholmFile extends AlignFile
     super(inFile, type);\r
   }\r
 \r
+  public StockholmFile(FileParse source) throws IOException\r
+  {\r
+    super(source);\r
+  }\r
+\r
   public void initData()\r
   {\r
     super.initData();\r
@@ -72,6 +83,9 @@ public class StockholmFile extends AlignFile
     Hashtable seqs = new Hashtable();\r
     Regex p, r, rend, s, x;\r
 \r
+    // Temporary line for processing RNA annotation\r
+    // String RNAannot = "";\r
+\r
     // ------------------ Parsing File ----------------------\r
     // First, we have to check that this file has STOCKHOLM format, i.e. the\r
     // first line must match\r
@@ -88,18 +102,27 @@ public class StockholmFile extends AlignFile
     }\r
 \r
     // We define some Regexes here that will be used regularily later\r
-    rend = new Regex("\\/\\/"); // Find the end of an alignment\r
+    rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment\r
     p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in\r
-                                                // id/from/to\r
-    s = new Regex("(\\S+)\\s+(\\w{2})\\s+(.*)"); // Parses annotation subtype\r
+    // id/from/to\r
+    s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype\r
     r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line\r
     x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence\r
 \r
+    // Convert all bracket types to parentheses (necessary for passing to VARNA)\r
+    Regex openparen = new Regex("(<|\\[)", "(");\r
+    Regex closeparen = new Regex("(>|\\])", ")");\r
+\r
+    // Detect if file is RNA by looking for bracket types\r
+    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");\r
+\r
     rend.optimize();\r
     p.optimize();\r
     s.optimize();\r
     r.optimize();\r
     x.optimize();\r
+    openparen.optimize();\r
+    closeparen.optimize();\r
 \r
     while ((line = nextLine()) != null)\r
     {\r
@@ -118,7 +141,7 @@ public class StockholmFile extends AlignFile
         {\r
           String acc = (String) accs.nextElement();\r
           // logger.debug("Processing sequence " + acc);\r
-          String seq = (String) seqs.get(acc);\r
+          String seq = (String) seqs.remove(acc);\r
           if (maxLength < seq.length())\r
           {\r
             maxLength = seq.length();\r
@@ -126,12 +149,16 @@ public class StockholmFile extends AlignFile
           int start = 1;\r
           int end = -1;\r
           String sid = acc;\r
-          // Retrieve hash of annotations for this accession\r
+          /*\r
+           * Retrieve hash of annotations for this accession\r
+           * Associate Annotation with accession\r
+           */\r
           Hashtable accAnnotations = null;\r
 \r
           if (seqAnn != null && seqAnn.containsKey(acc))\r
           {\r
-            accAnnotations = (Hashtable) seqAnn.get(acc);\r
+            accAnnotations = (Hashtable) seqAnn.remove(acc);\r
+            //TODO: add structures to sequence\r
           }\r
 \r
           // Split accession in id and from/to\r
@@ -158,16 +185,27 @@ public class StockholmFile extends AlignFile
             {\r
               String src = dbr.substring(0, dbr.indexOf(";"));\r
               String acn = dbr.substring(dbr.indexOf(";") + 1);\r
-              DBRefEntry dbref = new DBRefEntry(jalview.util.DBRefUtils\r
-                      .getCanonicalName(src), acn, "");\r
-              seqO.addDBRef(dbref);\r
+              jalview.util.DBRefUtils.parseToDbRef(seqO, src, "0", acn);\r
+              // seqO.addDBRef(dbref);\r
             }\r
+          }        \r
+          if (accAnnotations != null && accAnnotations.containsKey("SS"))\r
+          {\r
+                 Vector v = (Vector) accAnnotations.get("SS");\r
+                 \r
+                 for (int i = 0; i < v.size(); i++)\r
+                   {\r
+                         AlignmentAnnotation an = (AlignmentAnnotation) v.elementAt(i);\r
+                         seqO.addAlignmentAnnotation(an);\r
+                         //annotations.add(an);\r
+                   }\r
           }\r
+        \r
           Hashtable features = null;\r
           // We need to adjust the positions of all features to account for gaps\r
           try\r
           {\r
-            features = (Hashtable) accAnnotations.get("features");\r
+            features = (Hashtable) accAnnotations.remove("features");\r
           } catch (java.lang.NullPointerException e)\r
           {\r
             // loggerwarn("Getting Features for " + acc + ": " +\r
@@ -177,6 +215,7 @@ public class StockholmFile extends AlignFile
           // if we have features\r
           if (features != null)\r
           {\r
+            int posmap[] = seqO.findPositionMap();\r
             Enumeration i = features.keys();\r
             while (i.hasMoreElements())\r
             {\r
@@ -185,8 +224,7 @@ public class StockholmFile extends AlignFile
               // TODO: parse out scores as annotation row\r
               // TODO: map coding region to core jalview feature types\r
               String type = i.nextElement().toString();\r
-              Hashtable content = (Hashtable) features.get(type);\r
-\r
+              Hashtable content = (Hashtable) features.remove(type);\r
               Enumeration j = content.keys();\r
               while (j.hasMoreElements())\r
               {\r
@@ -196,9 +234,15 @@ public class StockholmFile extends AlignFile
                 for (int k = 0; k < byChar.length; k++)\r
                 {\r
                   char c = byChar[k];\r
-                  if (!(c == ' ' || c == '_' || c == '-'))\r
+                  if (!(c == ' ' || c == '_' || c == '-' || c == '.')) // PFAM\r
+                  // uses\r
+                  // '.'\r
+                  // for\r
+                  // feature\r
+                  // background\r
                   {\r
-                    int new_pos = seqO.findPosition(k);\r
+                    int new_pos = posmap[k]; // look up nearest seqeunce\r
+                    // position to this column\r
                     SequenceFeature feat = new SequenceFeature(type, desc,\r
                             new_pos, new_pos, 0f, null);\r
 \r
@@ -210,10 +254,13 @@ public class StockholmFile extends AlignFile
             }\r
 \r
           }\r
+          // garbage collect\r
+\r
           // logger.debug("Adding seq " + acc + " from " + start + " to " + end\r
           // + ": " + seq);\r
           this.seqs.addElement(seqO);\r
         }\r
+        return; // finished parsing this segment of source\r
       }\r
       else if (!r.search(line))\r
       {\r
@@ -345,8 +392,8 @@ public class StockholmFile extends AlignFile
           if (x.search(annContent))\r
           {\r
             // parse out and create alignment annotation directly.\r
-            parseAnnotationRow(annotations, x.stringMatched(1), x\r
-                    .stringMatched(2));\r
+            parseAnnotationRow(annotations, x.stringMatched(1),\r
+                    x.stringMatched(2));\r
           }\r
         }\r
         else if (annType.equals("GR"))\r
@@ -365,14 +412,20 @@ public class StockholmFile extends AlignFile
           {\r
             String acc = s.stringMatched(1);\r
             String type = s.stringMatched(2);\r
-            String seq = s.stringMatched(3);\r
-            String description = new String();\r
-\r
+            String seq = new String(s.stringMatched(3));\r
+            String description = null;\r
             // Check for additional information about the current annotation\r
-            if (x.search(seq))\r
+            // We use a simple string tokenizer here for speed\r
+            StringTokenizer sep = new StringTokenizer(seq, " \t");\r
+            description = sep.nextToken();\r
+            if (sep.hasMoreTokens())\r
             {\r
-              description = x.stringMatched(1);\r
-              seq = x.stringMatched(2);\r
+              seq = sep.nextToken();\r
+            }\r
+            else\r
+            {\r
+              seq = description;\r
+              description = new String();\r
             }\r
             // sequence id with from-to fields\r
 \r
@@ -389,7 +442,7 @@ public class StockholmFile extends AlignFile
               ann = new Hashtable();\r
               seqAnn.put(acc, ann);\r
             }\r
-\r
+            //TODO test structure, call parseAnnotationRow with vector from hashtable for specific sequence\r
             Hashtable features;\r
             // Get an object with all the content for an annotation\r
             if (ann.containsKey("features"))\r
@@ -423,11 +476,32 @@ public class StockholmFile extends AlignFile
               ns = "";\r
             }\r
             ns += seq;\r
-            content.put(description, seq);\r
+            content.put(description, ns);\r
+
+            if(type.equals("SS")){\r
+                Hashtable strucAnn;\r
+                if (seqAnn.containsKey(acc))\r
+                {\r
+                  strucAnn = (Hashtable) seqAnn.get(acc);\r
+                }\r
+                else\r
+                {\r
+                  strucAnn = new Hashtable();\r
+                }\r
+                \r
+                Vector newStruc=new Vector();\r
+                parseAnnotationRow(newStruc, type,ns);\r
+                \r
+                strucAnn.put(type, newStruc);\r
+                seqAnn.put(acc, strucAnn);\r
+             }\r
           }\r
           else\r
           {\r
-            throw new IOException("Error parsing " + line);\r
+            System.err\r
+                    .println("Warning - couldn't parse sequence annotation row line:\n"\r
+                            + line);\r
+            // throw new IOException("Error parsing " + line);\r
           }\r
         }\r
         else\r
@@ -450,6 +524,19 @@ public class StockholmFile extends AlignFile
   private AlignmentAnnotation parseAnnotationRow(Vector annotation,\r
           String label, String annots)\r
   {\r
+    String convert1, convert2 = null;\r
+\r
+    // Convert all bracket types to parentheses\r
+    Regex openparen = new Regex("(<|\\[)", "(");\r
+    Regex closeparen = new Regex("(>|\\])", ")");\r
+\r
+    // Detect if file is RNA by looking for bracket types\r
+    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");\r
+\r
+    convert1 = openparen.replaceAll(annots);\r
+    convert2 = closeparen.replaceAll(convert1);\r
+    annots = convert2;\r
+\r
     String type = (label.indexOf("_cons") == label.length() - 5) ? label\r
             .substring(0, label.length() - 5) : label;\r
     boolean ss = false;\r
@@ -464,18 +551,28 @@ public class StockholmFile extends AlignFile
     {\r
       String pos = annots.substring(i, i + 1);\r
       Annotation ann;\r
-      ann = new Annotation(pos, "", ' ', Float.NaN);\r
+      ann = new Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not\r
+      // be written out\r
       if (ss)\r
       {\r
-        ann.secondaryStructure = jalview.schemes.ResidueProperties\r
-                .getDssp3state(pos).charAt(0);\r
+        if (detectbrackets.search(pos))\r
+        {\r
+          ann.secondaryStructure = jalview.schemes.ResidueProperties\r
+                  .getRNASecStrucState(pos).charAt(0);\r
+        }\r
+        else\r
+        {\r
+          ann.secondaryStructure = jalview.schemes.ResidueProperties\r
+                  .getDssp3state(pos).charAt(0);\r
+        }\r
+\r
         if (ann.secondaryStructure == pos.charAt(0) || pos.charAt(0) == 'C')\r
         {\r
-          ann.displayCharacter = "";\r
+          ann.displayCharacter = ""; // null; // " ";\r
         }\r
         else\r
         {\r
-          ann.displayCharacter += " ";\r
+          ann.displayCharacter = " " + ann.displayCharacter;\r
         }\r
       }\r
 \r
@@ -503,6 +600,7 @@ public class StockholmFile extends AlignFile
               annot.annotations.length);\r
       System.arraycopy(els, 0, anns, annot.annotations.length, els.length);\r
       annot.annotations = anns;\r
+      //System.out.println("else: ");\r
     }\r
     return annot;\r
   }\r
@@ -552,4 +650,38 @@ public class StockholmFile extends AlignFile
             + id);\r
     return id;\r
   }\r
+  /**\r
+   * //ssline is complete secondary structure line private AlignmentAnnotation\r
+   * addHelices(Vector annotation, String label, String ssline) {\r
+   * \r
+   * // decide on secondary structure or not. Annotation[] els = new\r
+   * Annotation[ssline.length()]; for (int i = 0; i < ssline.length(); i++) {\r
+   * String pos = ssline.substring(i, i + 1); Annotation ann; ann = new\r
+   * Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not\r
+   * \r
+   * ann.secondaryStructure =\r
+   * jalview.schemes.ResidueProperties.getRNAssState(pos).charAt(0);\r
+   * \r
+   * ann.displayCharacter = "x" + ann.displayCharacter;\r
+   * \r
+   * System.out.println(ann.displayCharacter);\r
+   * \r
+   * els[i] = ann; } AlignmentAnnotation helicesAnnot = null; Enumeration e =\r
+   * annotation.elements(); while (e.hasMoreElements()) { helicesAnnot =\r
+   * (AlignmentAnnotation) e.nextElement(); if (helicesAnnot.label.equals(type))\r
+   * break; helicesAnnot = null; } if (helicesAnnot == null) { helicesAnnot =\r
+   * new AlignmentAnnotation(type, type, els);\r
+   * annotation.addElement(helicesAnnot); } else { Annotation[] anns = new\r
+   * Annotation[helicesAnnot.annotations.length + els.length];\r
+   * System.arraycopy(helicesAnnot.annotations, 0, anns, 0,\r
+   * helicesAnnot.annotations.length); System.arraycopy(els, 0, anns,\r
+   * helicesAnnot.annotations.length, els.length); helicesAnnot.annotations =\r
+   * anns; }\r
+   * \r
+   * helicesAnnot.features = Rna.GetBasePairs(ssline);\r
+   * Rna.HelixMap(helicesAnnot.features);\r
+   * \r
+   * \r
+   * return helicesAnnot; }\r
+   */\r
 }\r