Merge branch 'docs/2_8_1_Release' into Release_2_8_2_Branch
[jalview.git] / src / jalview / io / StockholmFile.java
index 9b5a5cd..e9c4ed1 100644 (file)
  */
 package jalview.io;
 
-import java.io.*;
-import java.util.*;
-
-import com.stevesoft.pat.*;
-import jalview.datamodel.*;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
 import jalview.util.Format;
 
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import com.stevesoft.pat.Regex;
+
+import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
+import fr.orsay.lri.varna.factories.RNAFactory;
+import fr.orsay.lri.varna.models.rna.RNA;
+
 // import org.apache.log4j.*;
 
 /**
@@ -39,12 +59,16 @@ import jalview.util.Format;
  * into Jalview's local representation.
  * 
  * @author bsb at sanger.ac.uk
+ * @author Natasha Shersnev (Dundee, UK) (Stockholm file writer)
+ * @author Lauren Lui (UCSC, USA) (RNA secondary structure annotation import as stockholm)
+ * @author Anne Menard (Paris, FR) (VARNA parsing of Stockholm file data)
  * @version 0.3 + jalview mods
  * 
  */
 public class StockholmFile extends AlignFile
 {
   // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
+  protected ArrayList<RNA> result;
   StringBuffer out; // output buffer
 
   AlignmentI al;
@@ -75,78 +99,145 @@ public class StockholmFile extends AlignFile
   {
     super.initData();
   }
-
   /**
-   * Parse a file in Stockholm format into Jalview's data model. The file has to
-   * be passed at construction time
+   * Parse a file in Stockholm format into Jalview's data model using VARNA
    * 
    * @throws IOException
    *           If there is an error with the input file
    */
-  public void parse() throws IOException
+  public void parse_with_VARNA(java.io.File inFile) throws IOException
   {
-    StringBuffer treeString = new StringBuffer();
-    String treeName = null;
-    // --------------- Variable Definitions -------------------
-    String line;
-    String version;
-    // String id;
-    Hashtable seqAnn = new Hashtable(); // Sequence related annotations
-    Hashtable seqs = new Hashtable();
-    Regex p, r, rend, s, x;
+    FileReader fr = null;
+    fr = new FileReader(inFile);
 
-    // Temporary line for processing RNA annotation
-    // String RNAannot = "";
-
-    // ------------------ Parsing File ----------------------
-    // First, we have to check that this file has STOCKHOLM format, i.e. the
-    // first line must match
-    r = new Regex("# STOCKHOLM ([\\d\\.]+)");
-    if (!r.search(nextLine()))
+    BufferedReader r = new BufferedReader(fr);
+    result = null;
+    try
     {
-      throw new IOException(
-              "This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM'");
-    }
-    else
+      result = RNAFactory.loadSecStrStockholm(r);
+    } catch (ExceptionUnmatchedClosingParentheses umcp)
     {
-      version = r.stringMatched(1);
-      // logger.debug("Stockholm version: " + version);
+      errormessage = "Unmatched parentheses in annotation. Aborting ("
+              + umcp.getMessage() + ")";
+      throw new IOException(umcp);
     }
+    // DEBUG System.out.println("this is the secondary scructure:"
+    // +result.size());
+    SequenceI[] seqs = new SequenceI[result.size()];
+    String id=null;
+    for (int i = 0; i < result.size(); i++)
+    {
+      // DEBUG System.err.println("Processing i'th sequence in Stockholm file")
+      RNA current = result.get(i);
+
+      String seq = current.getSeq();
+      String rna = current.getStructDBN(true);
+      // DEBUG System.out.println(seq);
+      // DEBUG System.err.println(rna);
+      int begin = 0;
+      int end = seq.length() - 1;
+      id = safeName(getDataName());
+      seqs[i] = new Sequence(id, seq, begin, end);
+      String[] annot = new String[rna.length()];
+      Annotation[] ann = new Annotation[rna.length()];
+      for (int j = 0; j < rna.length(); j++)
+      {
+        annot[j] = rna.substring(j, j + 1);
 
-    // We define some Regexes here that will be used regularily later
-    rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment
-    p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
-    // id/from/to
-    s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype
-    r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line
-    x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence
+      }
 
-    // Convert all bracket types to parentheses (necessary for passing to VARNA)
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
+      for (int k = 0; k < rna.length(); k++)
+      {
+        ann[k] = new Annotation(annot[k], "",
+                jalview.schemes.ResidueProperties.getRNASecStrucState(
+                        annot[k]).charAt(0), 0f);
 
-    // Detect if file is RNA by looking for bracket types
-    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
+      }
+      AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.",
+              current.getID(), ann);
 
-    rend.optimize();
-    p.optimize();
-    s.optimize();
-    r.optimize();
-    x.optimize();
-    openparen.optimize();
-    closeparen.optimize();
+      seqs[i].addAlignmentAnnotation(align);
+      seqs[i].setRNA(result.get(i));
+      this.annotations.addElement(align);
+    }
+    this.setSeqs(seqs);
 
-    while ((line = nextLine()) != null)
-    {
-      if (line.length() == 0)
-      {
-        continue;
-      }
-      if (rend.search(line))
-      {
-        // End of the alignment, pass stuff back
-        this.noSeqs = seqs.size();
+  }
 
+  
+  /**
+   * Parse a file in Stockholm format into Jalview's data model. The file has to
+   * be passed at construction time
+   * 
+   * @throws IOException
+   *           If there is an error with the input file
+   */
+  public void parse() throws IOException
+  {
+      StringBuffer treeString = new StringBuffer();
+      String treeName = null;
+      // --------------- Variable Definitions -------------------
+      String line;
+      String version;
+    // String id;
+      Hashtable seqAnn = new Hashtable(); // Sequence related annotations
+      Hashtable seqs = new Hashtable();
+      Regex p, r, rend, s, x;
+      // Temporary line for processing RNA annotation
+      // String RNAannot = "";
+
+      // ------------------ Parsing File ----------------------
+      // First, we have to check that this file has STOCKHOLM format, i.e. the
+      // first line must match
+      
+  
+               r = new Regex("# STOCKHOLM ([\\d\\.]+)");
+               if (!r.search(nextLine()))
+               {
+                       throw new IOException(
+                                       "This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM'");
+               }
+               else
+               {
+                       version = r.stringMatched(1);
+               
+                       // logger.debug("Stockholm version: " + version);
+               }
+
+               // We define some Regexes here that will be used regularily later
+               rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment
+               p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
+               // id/from/to
+               s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype
+               r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line
+               x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence
+
+               // Convert all bracket types to parentheses (necessary for passing to VARNA)
+               Regex openparen = new Regex("(<|\\[)", "(");
+               Regex closeparen = new Regex("(>|\\])", ")");
+
+               // Detect if file is RNA by looking for bracket types
+               Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
+
+               rend.optimize();
+           p.optimize();
+           s.optimize();
+           r.optimize();
+           x.optimize();
+           openparen.optimize();
+           closeparen.optimize();
+       
+           while ((line = nextLine()) != null)
+           {
+             if (line.length() == 0)
+             {
+               continue;
+             }
+             if (rend.search(line))
+             {
+               // End of the alignment, pass stuff back
+        this.noSeqs = seqs.size();
+       
         String seqdb,dbsource = null;
         Regex pf = new Regex("PF[0-9]{5}(.*)"); // Finds AC for Pfam
         Regex rf = new Regex("RF[0-9]{5}(.*)"); // Finds AC for Rfam
@@ -163,60 +254,59 @@ public class StockholmFile extends AlignFile
             dbsource = "RFAM";
           }
         }
-        // logger.debug("Number of sequences: " + this.noSeqs);
-        Enumeration accs = seqs.keys();
-        while (accs.hasMoreElements())
-        {
-          String acc = (String) accs.nextElement();
-          // logger.debug("Processing sequence " + acc);
-          String seq = (String) seqs.remove(acc);
-          if (maxLength < seq.length())
-          {
-            maxLength = seq.length();
-          }
-          int start = 1;
-          int end = -1;
-          String sid = acc;
-          /*
+               // logger.debug("Number of sequences: " + this.noSeqs);
+               Enumeration accs = seqs.keys();
+               while (accs.hasMoreElements())
+               {
+                 String acc = (String) accs.nextElement();
+                 // logger.debug("Processing sequence " + acc);
+                 String seq = (String) seqs.remove(acc);
+                 if (maxLength < seq.length())
+                 {
+                   maxLength = seq.length();
+                 }
+                 int start = 1;
+                 int end = -1;
+                 String sid = acc;
+                 /*
            * Retrieve hash of annotations for this accession Associate
            * Annotation with accession
-           */
-          Hashtable accAnnotations = null;
-
-          if (seqAnn != null && seqAnn.containsKey(acc))
-          {
-            accAnnotations = (Hashtable) seqAnn.remove(acc);
-            // TODO: add structures to sequence
-          }
-
-          // Split accession in id and from/to
-          if (p.search(acc))
-          {
-            sid = p.stringMatched(1);
-            start = Integer.parseInt(p.stringMatched(2));
-            end = Integer.parseInt(p.stringMatched(3));
-          }
-          // logger.debug(sid + ", " + start + ", " + end);
-
-          Sequence seqO = new Sequence(sid, seq, start, end);
-          // Add Description (if any)
-          if (accAnnotations != null && accAnnotations.containsKey("DE"))
-          {
-            String desc = (String) accAnnotations.get("DE");
-            seqO.setDescription((desc == null) ? "" : desc);
-          }
-
-          // Add DB References (if any)
-          if (accAnnotations != null && accAnnotations.containsKey("DR"))
-          {
-            String dbr = (String) accAnnotations.get("DR");
-            if (dbr != null && dbr.indexOf(";") > -1)
-            {
-              String src = dbr.substring(0, dbr.indexOf(";"));
-              String acn = dbr.substring(dbr.indexOf(";") + 1);
-              jalview.util.DBRefUtils.parseToDbRef(seqO, src, "0", acn);
-            }
-          }
+                  */
+                 Hashtable accAnnotations = null;
+       
+                 if (seqAnn != null && seqAnn.containsKey(acc))
+                 {
+                   accAnnotations = (Hashtable) seqAnn.remove(acc);
+                   //TODO: add structures to sequence
+                 }
+       
+                 // Split accession in id and from/to
+                 if (p.search(acc))
+                 {
+                   sid = p.stringMatched(1);
+                   start = Integer.parseInt(p.stringMatched(2));
+                   end = Integer.parseInt(p.stringMatched(3));
+                 }
+                 // logger.debug(sid + ", " + start + ", " + end);
+       
+                 Sequence seqO = new Sequence(sid, seq, start, end);
+                 // Add Description (if any)
+                 if (accAnnotations != null && accAnnotations.containsKey("DE"))
+                 {
+                   String desc = (String) accAnnotations.get("DE");
+                   seqO.setDescription((desc == null) ? "" : desc);
+                 }
+                 // Add DB References (if any)
+                 if (accAnnotations != null && accAnnotations.containsKey("DR"))
+                 {
+                   String dbr = (String) accAnnotations.get("DR");
+                   if (dbr != null && dbr.indexOf(";") > -1)
+                   {
+                     String src = dbr.substring(0, dbr.indexOf(";"));
+                     String acn = dbr.substring(dbr.indexOf(";") + 1);
+                     jalview.util.DBRefUtils.parseToDbRef(seqO, src, "0", acn);
+                   }
+                 }        
 
           if (accAnnotations != null && accAnnotations.containsKey("AC"))
           {
@@ -224,39 +314,39 @@ public class StockholmFile extends AlignFile
             {
               String dbr = (String) accAnnotations.get("AC");
               if (dbr != null)
-              {
+                 {
                 // we could get very clever here - but for now - just try to guess accession type from source of alignment plus structure of accession
                 guessDatabaseFor(seqO, dbr, dbsource);
-                
-              }
-            } 
+                         
+                           }
+                 }
             // else - do what ?  add the data anyway and prompt the user to specify what references these are ?
           }
-
-          Hashtable features = null;
-          // We need to adjust the positions of all features to account for gaps
-          try
-          {
-            features = (Hashtable) accAnnotations.remove("features");
-          } catch (java.lang.NullPointerException e)
-          {
-            // loggerwarn("Getting Features for " + acc + ": " +
-            // e.getMessage());
-            // continue;
-          }
-          // if we have features
-          if (features != null)
-          {
-            int posmap[] = seqO.findPositionMap();
-            Enumeration i = features.keys();
-            while (i.hasMoreElements())
-            {
-              // TODO: parse out secondary structure annotation as annotation
-              // row
-              // TODO: parse out scores as annotation row
-              // TODO: map coding region to core jalview feature types
-              String type = i.nextElement().toString();
-              Hashtable content = (Hashtable) features.remove(type);
+               
+                 Hashtable features = null;
+                 // We need to adjust the positions of all features to account for gaps
+                 try
+                 {
+                   features = (Hashtable) accAnnotations.remove("features");
+                 } catch (java.lang.NullPointerException e)
+                 {
+                   // loggerwarn("Getting Features for " + acc + ": " +
+                   // e.getMessage());
+                   // continue;
+                 }
+                 // if we have features
+                 if (features != null)
+                 {
+                   int posmap[] = seqO.findPositionMap();
+                   Enumeration i = features.keys();
+                   while (i.hasMoreElements())
+                   {
+                     // TODO: parse out secondary structure annotation as annotation
+                     // row
+                     // TODO: parse out scores as annotation row
+                     // TODO: map coding region to core jalview feature types
+                     String type = i.nextElement().toString();
+                     Hashtable content = (Hashtable) features.remove(type);
 
               // add alignment annotation for this feature
               String key = type2id(type);
@@ -275,300 +365,304 @@ public class StockholmFile extends AlignFile
                 }
               }
 
-              Enumeration j = content.keys();
-              while (j.hasMoreElements())
-              {
-                String desc = j.nextElement().toString();
-                String ns = content.get(desc).toString();
-                char[] byChar = ns.toCharArray();
-                for (int k = 0; k < byChar.length; k++)
-                {
-                  char c = byChar[k];
-                  if (!(c == ' ' || c == '_' || c == '-' || c == '.')) // PFAM
-                  // uses
-                  // '.'
-                  // for
-                  // feature
-                  // background
-                  {
-                    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);
-
-                    seqO.addSequenceFeature(feat);
-                  }
-                }
-              }
-
-            }
-
-          }
-          // garbage collect
-
-          // logger.debug("Adding seq " + acc + " from " + start + " to " + end
-          // + ": " + seq);
-          this.seqs.addElement(seqO);
-        }
-        return; // finished parsing this segment of source
-      }
-      else if (!r.search(line))
-      {
-        // System.err.println("Found sequence line: " + line);
-
-        // Split sequence in sequence and accession parts
-        if (!x.search(line))
-        {
-          // logger.error("Could not parse sequence line: " + line);
-          throw new IOException("Could not parse sequence line: " + line);
-        }
-        String ns = (String) seqs.get(x.stringMatched(1));
-        if (ns == null)
-        {
-          ns = "";
-        }
-        ns += x.stringMatched(2);
-
-        seqs.put(x.stringMatched(1), ns);
-      }
-      else
-      {
-        String annType = r.stringMatched(1);
-        String annContent = r.stringMatched(2);
-
-        // System.err.println("type:" + annType + " content: " + annContent);
-
-        if (annType.equals("GF"))
-        {
-          /*
-           * Generic per-File annotation, free text Magic features: #=GF NH
-           * <tree in New Hampshire eXtended format> #=GF TN <Unique identifier
-           * for the next tree> Pfam descriptions: 7. DESCRIPTION OF FIELDS
-           * 
-           * Compulsory fields: ------------------
-           * 
-           * AC Accession number: Accession number in form PFxxxxx.version or
-           * PBxxxxxx. ID Identification: One word name for family. DE
-           * Definition: Short description of family. AU Author: Authors of the
-           * entry. SE Source of seed: The source suggesting the seed members
-           * belong to one family. GA Gathering method: Search threshold to
-           * build the full alignment. TC Trusted Cutoff: Lowest sequence score
-           * and domain score of match in the full alignment. NC Noise Cutoff:
-           * Highest sequence score and domain score of match not in full
-           * alignment. TP Type: Type of family -- presently Family, Domain,
-           * Motif or Repeat. SQ Sequence: Number of sequences in alignment. AM
-           * Alignment Method The order ls and fs hits are aligned to the model
-           * to build the full align. // End of alignment.
-           * 
-           * Optional fields: ----------------
-           * 
-           * DC Database Comment: Comment about database reference. DR Database
-           * Reference: Reference to external database. RC Reference Comment:
-           * Comment about literature reference. RN Reference Number: Reference
-           * Number. RM Reference Medline: Eight digit medline UI number. RT
-           * Reference Title: Reference Title. RA Reference Author: Reference
-           * Author RL Reference Location: Journal location. PI Previous
-           * identifier: Record of all previous ID lines. KW Keywords: Keywords.
-           * CC Comment: Comments. NE Pfam accession: Indicates a nested domain.
-           * NL Location: Location of nested domains - sequence ID, start and
-           * end of insert.
-           * 
-           * Obsolete fields: ----------- AL Alignment method of seed: The
-           * method used to align the seed members.
-           */
-          // Let's save the annotations, maybe we'll be able to do something
-          // with them later...
-          Regex an = new Regex("(\\w+)\\s*(.*)");
-          if (an.search(annContent))
-          {
-            if (an.stringMatched(1).equals("NH"))
-            {
-              treeString.append(an.stringMatched(2));
-            }
-            else if (an.stringMatched(1).equals("TN"))
-            {
-              if (treeString.length() > 0)
-              {
-                if (treeName == null)
-                {
-                  treeName = "Tree " + (getTreeCount() + 1);
-                }
-                addNewickTree(treeName, treeString.toString());
-              }
-              treeName = an.stringMatched(2);
-              treeString = new StringBuffer();
-            }
-            setAlignmentProperty(an.stringMatched(1), an.stringMatched(2));
-          }
-        }
-        else if (annType.equals("GS"))
-        {
-          // Generic per-Sequence annotation, free text
-          /*
-           * Pfam uses these features: Feature Description ---------------------
-           * ----------- AC <accession> ACcession number DE <freetext>
-           * DEscription DR <db>; <accession>; Database Reference OS <organism>
-           * OrganiSm (species) OC <clade> Organism Classification (clade, etc.)
-           * LO <look> Look (Color, etc.)
-           */
-          if (s.search(annContent))
-          {
-            String acc = s.stringMatched(1);
-            String type = s.stringMatched(2);
-            String content = s.stringMatched(3);
-            // TODO: store DR in a vector.
-            // TODO: store AC according to generic file db annotation.
-            Hashtable ann;
-            if (seqAnn.containsKey(acc))
-            {
-              ann = (Hashtable) seqAnn.get(acc);
-            }
-            else
-            {
-              ann = new Hashtable();
-            }
-            ann.put(type, content);
-            seqAnn.put(acc, ann);
-          }
-          else
-          {
-            throw new IOException("Error parsing " + line);
-          }
-        }
-        else if (annType.equals("GC"))
-        {
-          // Generic per-Column annotation, exactly 1 char per column
-          // always need a label.
-          if (x.search(annContent))
-          {
-            // parse out and create alignment annotation directly.
-            parseAnnotationRow(annotations, x.stringMatched(1),
-                    x.stringMatched(2));
-          }
-        }
-        else if (annType.equals("GR"))
-        {
-          // Generic per-Sequence AND per-Column markup, exactly 1 char per
-          // column
-          /*
-           * Feature Description Markup letters ------- -----------
-           * -------------- SS Secondary Structure [HGIEBTSCX] SA Surface
-           * Accessibility [0-9X] (0=0%-10%; ...; 9=90%-100%) TM TransMembrane
-           * [Mio] PP Posterior Probability [0-9*] (0=0.00-0.05; 1=0.05-0.15;
-           * *=0.95-1.00) LI LIgand binding [*] AS Active Site [*] IN INtron (in
-           * or after) [0-2]
-           */
-          if (s.search(annContent))
-          {
-            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
-
-            Hashtable ann;
-            // Get an object with all the annotations for this sequence
-            if (seqAnn.containsKey(acc))
-            {
-              // logger.debug("Found annotations for " + acc);
-              ann = (Hashtable) seqAnn.get(acc);
-            }
-            else
-            {
-              // logger.debug("Creating new annotations holder for " + acc);
-              ann = new Hashtable();
-              seqAnn.put(acc, ann);
-            }
+                     Enumeration j = content.keys();
+                     while (j.hasMoreElements())
+                     {
+                       String desc = j.nextElement().toString();
+                       String ns = content.get(desc).toString();
+                       char[] byChar = ns.toCharArray();
+                       for (int k = 0; k < byChar.length; k++)
+                       {
+                         char c = byChar[k];
+                         if (!(c == ' ' || c == '_' || c == '-' || c == '.')) // PFAM
+                         // uses
+                         // '.'
+                         // for
+                         // feature
+                         // background
+                         {
+                           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);
+       
+                           seqO.addSequenceFeature(feat);
+                         }
+                       }
+                     }
+       
+                   }
+       
+                 }
+                 // garbage collect
+       
+                 // logger.debug("Adding seq " + acc + " from " + start + " to " + end
+                 // + ": " + seq);
+                 this.seqs.addElement(seqO);
+               }
+               return; // finished parsing this segment of source
+             }
+             else if (!r.search(line))
+             {
+               // System.err.println("Found sequence line: " + line);
+       
+               // Split sequence in sequence and accession parts
+               if (!x.search(line))
+               {
+                 // logger.error("Could not parse sequence line: " + line);
+                 throw new IOException("Could not parse sequence line: " + line);
+               }
+               String ns = (String) seqs.get(x.stringMatched(1));
+               if (ns == null)
+               {
+                 ns = "";
+               }
+               ns += x.stringMatched(2);
+       
+               seqs.put(x.stringMatched(1), ns);
+             }
+             else
+             {
+               String annType = r.stringMatched(1);
+               String annContent = r.stringMatched(2);
+       
+               // System.err.println("type:" + annType + " content: " + annContent);
+       
+               if (annType.equals("GF"))
+               {
+                 /*
+                  * Generic per-File annotation, free text Magic features: #=GF NH
+                  * <tree in New Hampshire eXtended format> #=GF TN <Unique identifier
+                  * for the next tree> Pfam descriptions: 7. DESCRIPTION OF FIELDS
+                  * 
+                  * Compulsory fields: ------------------
+                  * 
+                  * AC Accession number: Accession number in form PFxxxxx.version or
+                  * PBxxxxxx. ID Identification: One word name for family. DE
+                  * Definition: Short description of family. AU Author: Authors of the
+                  * entry. SE Source of seed: The source suggesting the seed members
+                  * belong to one family. GA Gathering method: Search threshold to
+                  * build the full alignment. TC Trusted Cutoff: Lowest sequence score
+                  * and domain score of match in the full alignment. NC Noise Cutoff:
+                  * Highest sequence score and domain score of match not in full
+                  * alignment. TP Type: Type of family -- presently Family, Domain,
+                  * Motif or Repeat. SQ Sequence: Number of sequences in alignment. AM
+                  * Alignment Method The order ls and fs hits are aligned to the model
+                  * to build the full align. // End of alignment.
+                  * 
+                  * Optional fields: ----------------
+                  * 
+                  * DC Database Comment: Comment about database reference. DR Database
+                  * Reference: Reference to external database. RC Reference Comment:
+                  * Comment about literature reference. RN Reference Number: Reference
+                  * Number. RM Reference Medline: Eight digit medline UI number. RT
+                  * Reference Title: Reference Title. RA Reference Author: Reference
+                  * Author RL Reference Location: Journal location. PI Previous
+                  * identifier: Record of all previous ID lines. KW Keywords: Keywords.
+                  * CC Comment: Comments. NE Pfam accession: Indicates a nested domain.
+                  * NL Location: Location of nested domains - sequence ID, start and
+                  * end of insert.
+                  * 
+                  * Obsolete fields: ----------- AL Alignment method of seed: The
+                  * method used to align the seed members.
+                  */
+                 // Let's save the annotations, maybe we'll be able to do something
+                 // with them later...
+                 Regex an = new Regex("(\\w+)\\s*(.*)");
+                 if (an.search(annContent))
+                 {
+                   if (an.stringMatched(1).equals("NH"))
+                   {
+                     treeString.append(an.stringMatched(2));
+                   }
+                   else if (an.stringMatched(1).equals("TN"))
+                   {
+                     if (treeString.length() > 0)
+                     {
+                       if (treeName == null)
+                       {
+                         treeName = "Tree " + (getTreeCount() + 1);
+                       }
+                       addNewickTree(treeName, treeString.toString());
+                     }
+                     treeName = an.stringMatched(2);
+                     treeString = new StringBuffer();
+                   }
+                   setAlignmentProperty(an.stringMatched(1), an.stringMatched(2));
+                 }
+               }
+               else if (annType.equals("GS"))
+               {
+                 // Generic per-Sequence annotation, free text
+                 /*
+                  * Pfam uses these features: Feature Description ---------------------
+                  * ----------- AC <accession> ACcession number DE <freetext>
+                  * DEscription DR <db>; <accession>; Database Reference OS <organism>
+                  * OrganiSm (species) OC <clade> Organism Classification (clade, etc.)
+                  * LO <look> Look (Color, etc.)
+                  */
+                 if (s.search(annContent))
+                 {
+                   String acc = s.stringMatched(1);
+                   String type = s.stringMatched(2);
+                   String content = s.stringMatched(3);
+                   // TODO: store DR in a vector.
+                   // TODO: store AC according to generic file db annotation.
+                   Hashtable ann;
+                   if (seqAnn.containsKey(acc))
+                   {
+                     ann = (Hashtable) seqAnn.get(acc);
+                   }
+                   else
+                   {
+                     ann = new Hashtable();
+                   }
+                   ann.put(type, content);
+                   seqAnn.put(acc, ann);
+                 }
+                 else
+                 {
+                   throw new IOException("Error parsing " + line);
+                 }
+               }
+               else if (annType.equals("GC"))
+               {
+                 // Generic per-Column annotation, exactly 1 char per column
+                 // always need a label.
+                 if (x.search(annContent))
+                 {
+                   // parse out and create alignment annotation directly.
+                   parseAnnotationRow(annotations, x.stringMatched(1),
+                           x.stringMatched(2));
+                 }
+               }
+               else if (annType.equals("GR"))
+               {
+                 // Generic per-Sequence AND per-Column markup, exactly 1 char per
+                 // column
+                 /*
+                  * Feature Description Markup letters ------- -----------
+                  * -------------- SS Secondary Structure [HGIEBTSCX] SA Surface
+                  * Accessibility [0-9X] (0=0%-10%; ...; 9=90%-100%) TM TransMembrane
+                  * [Mio] PP Posterior Probability [0-9*] (0=0.00-0.05; 1=0.05-0.15;
+                  * *=0.95-1.00) LI LIgand binding [*] AS Active Site [*] IN INtron (in
+                  * or after) [0-2]
+                  */
+                 if (s.search(annContent))
+                 {
+                   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
+       
+                   Hashtable ann;
+                   // Get an object with all the annotations for this sequence
+                   if (seqAnn.containsKey(acc))
+                   {
+                     // logger.debug("Found annotations for " + acc);
+                     ann = (Hashtable) seqAnn.get(acc);
+                   }
+                   else
+                   {
+                     // logger.debug("Creating new annotations holder for " + acc);
+                     ann = new Hashtable();
+                     seqAnn.put(acc, ann);
+                   }
             // 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"))
-            {
-              // logger.debug("Found features for " + acc);
-              features = (Hashtable) ann.get("features");
-            }
-            else
-            {
-              // logger.debug("Creating new features holder for " + acc);
-              features = new Hashtable();
-              ann.put("features", features);
-            }
+                   Hashtable features;
+                   // Get an object with all the content for an annotation
+                   if (ann.containsKey("features"))
+                   {
+                     // logger.debug("Found features for " + acc);
+                     features = (Hashtable) ann.get("features");
+                   }
+                   else
+                   {
+                     // logger.debug("Creating new features holder for " + acc);
+                     features = new Hashtable();
+                     ann.put("features", features);
+                   }
+       
+                   Hashtable content;
+                   if (features.containsKey(this.id2type(type)))
+                   {
+                     // logger.debug("Found content for " + this.id2type(type));
+                     content = (Hashtable) features.get(this.id2type(type));
+                   }
+                   else
+                   {
+                     // logger.debug("Creating new content holder for " +
+                     // this.id2type(type));
+                     content = new Hashtable();
+                     features.put(this.id2type(type), content);
+                   }
+                   String ns = (String) content.get(description);
+                   if (ns == null)
+                   {
+                     ns = "";
+                   }
+                   ns += seq;
+                   content.put(description, ns);
+       
+//                 if(type.equals("SS")){
+                       Hashtable strucAnn;
+                       if (seqAnn.containsKey(acc))
+                       {
+                         strucAnn = (Hashtable) seqAnn.get(acc);
+                       }
+                       else
+                       {
+                         strucAnn = new Hashtable();
+                       }
+                       
+                       Vector newStruc=new Vector();
+                       parseAnnotationRow(newStruc, type,ns);
+                       
+                       strucAnn.put(type, newStruc);
+                       seqAnn.put(acc, strucAnn);
+                    }
+//               }
+                       else
+                       {
+                                               System.err
+                                               .println("Warning - couldn't parse sequence annotation row line:\n"
+                                               + line);
+                       // throw new IOException("Error parsing " + line);
+                       }
+                       }
+                       else
+                       {
+                       throw new IOException("Unknown annotation detected: " + annType
+                               + " " + annContent);
+                       }
+                       }
+               }
+               if (treeString.length() > 0)
+               {
+               if (treeName == null)
+               {
+                       treeName = "Tree " + (1 + getTreeCount());
+               }
+               addNewickTree(treeName, treeString.toString());
+               }
+       }
 
-            Hashtable content;
-            if (features.containsKey(this.id2type(type)))
-            {
-              // logger.debug("Found content for " + this.id2type(type));
-              content = (Hashtable) features.get(this.id2type(type));
-            }
-            else
-            {
-              // logger.debug("Creating new content holder for " +
-              // this.id2type(type));
-              content = new Hashtable();
-              features.put(this.id2type(type), content);
-            }
-            String ns = (String) content.get(description);
-            if (ns == null)
-            {
-              ns = "";
-            }
-            ns += seq;
-            content.put(description, ns);
-            Hashtable strucAnn;
-            if (seqAnn.containsKey(acc))
-            {
-              strucAnn = (Hashtable) seqAnn.get(acc);
-            }
-            else
-            {
-              strucAnn = new Hashtable();
-            }
-
-            Vector newStruc = new Vector();
-            parseAnnotationRow(newStruc, type, ns);
-            strucAnn.put(type, newStruc);
-            seqAnn.put(acc, strucAnn);
-          }
-          else
-          {
-            System.err
-                    .println("Warning - couldn't parse sequence annotation row line:\n"
-                            + line);
-            // throw new IOException("Error parsing " + line);
-          }
-        }
-        else
-        {
-          throw new IOException("Unknown annotation detected: " + annType
-                  + " " + annContent);
-        }
-      }
-    }
-    if (treeString.length() > 0)
-    {
-      if (treeName == null)
-      {
-        treeName = "Tree " + (1 + getTreeCount());
-      }
-      addNewickTree(treeName, treeString.toString());
-    }
-  }
-
-  /**
+/**
    * Demangle an accession string and guess the originating sequence database for a given sequence
    * @param seqO sequence to be annotated
    * @param dbr Accession string for sequence
@@ -929,7 +1023,7 @@ public class StockholmFile extends AlignFile
             seq += ch;
           }
           else if (ch.length() > 1)
-          {
+  {
             seq += ch.charAt(1);
           }
         }
@@ -1010,37 +1104,21 @@ public class StockholmFile extends AlignFile
     return key;
   }
   /**
-   * //ssline is complete secondary structure line private AlignmentAnnotation
-   * addHelices(Vector annotation, String label, String ssline) {
-   * 
-   * // decide on secondary structure or not. Annotation[] els = new
-   * Annotation[ssline.length()]; for (int i = 0; i < ssline.length(); i++) {
-   * String pos = ssline.substring(i, i + 1); Annotation ann; ann = new
-   * Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not
+   * make a friendly ID string.
    * 
-   * ann.secondaryStructure =
-   * jalview.schemes.ResidueProperties.getRNAssState(pos).charAt(0);
-   * 
-   * ann.displayCharacter = "x" + ann.displayCharacter;
-   * 
-   * System.out.println(ann.displayCharacter);
-   * 
-   * els[i] = ann; } AlignmentAnnotation helicesAnnot = null; Enumeration e =
-   * annotation.elements(); while (e.hasMoreElements()) { helicesAnnot =
-   * (AlignmentAnnotation) e.nextElement(); if (helicesAnnot.label.equals(type))
-   * break; helicesAnnot = null; } if (helicesAnnot == null) { helicesAnnot =
-   * new AlignmentAnnotation(type, type, els);
-   * annotation.addElement(helicesAnnot); } else { Annotation[] anns = new
-   * Annotation[helicesAnnot.annotations.length + els.length];
-   * System.arraycopy(helicesAnnot.annotations, 0, anns, 0,
-   * helicesAnnot.annotations.length); System.arraycopy(els, 0, anns,
-   * helicesAnnot.annotations.length, els.length); helicesAnnot.annotations =
-   * anns; }
-   * 
-   * helicesAnnot.features = Rna.GetBasePairs(ssline);
-   * Rna.HelixMap(helicesAnnot.features);
-   * 
-   * 
-   * return helicesAnnot; }
+   * @param dataName
+   * @return truncated dataName to after last '/'
    */
+  private String safeName(String dataName)
+  {
+    int b = 0;
+    while ((b = dataName.indexOf("/")) > -1 && b < dataName.length())
+    {
+      dataName = dataName.substring(b + 1).trim();
+
+    }
+    int e = (dataName.length() - dataName.indexOf(".")) + 1;
+    dataName = dataName.substring(1, e).trim();
+    return dataName;
+  }
 }