stockholm file output code developed by Natasha Sherstneva
[jalview.git] / src / jalview / io / StockholmFile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /*
19  * This extension was written by Benjamin Schuster-Boeckler at sanger.ac.uk
20  */
21 package jalview.io;
22
23 import java.io.*;
24 import java.util.*;
25
26 import com.stevesoft.pat.*;
27 import jalview.datamodel.*;
28 import jalview.util.Format;
29
30 // import org.apache.log4j.*;
31
32 /**
33  * This class is supposed to parse a Stockholm format file into Jalview There
34  * are TODOs in this class: we do not know what the database source and version
35  * is for the file when parsing the #GS= AC tag which associates accessions with
36  * sequences. Database references are also not parsed correctly: a separate
37  * reference string parser must be added to parse the database reference form
38  * into Jalview's local representation.
39  * 
40  * @author bsb at sanger.ac.uk
41  * @version 0.3 + jalview mods
42  * 
43  */
44 public class StockholmFile extends AlignFile
45 {
46   // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
47   StringBuffer out;
48   AlignmentI al;
49   
50   public StockholmFile()
51   {
52   }
53
54   /**
55   * Creates a new StockholmFile object for output.
56   */
57   public StockholmFile(AlignmentI al)
58   {
59     this.al = al;
60   }
61   
62   public StockholmFile(String inFile, String type) throws IOException
63   {
64     super(inFile, type);
65   }
66
67   public StockholmFile(FileParse source) throws IOException
68   {
69     super(source);
70   }
71
72   public void initData()
73   {
74     super.initData();
75   }
76
77   /**
78    * Parse a file in Stockholm format into Jalview's data model. The file has to
79    * be passed at construction time
80    * 
81    * @throws IOException
82    *           If there is an error with the input file
83    */
84   public void parse() throws IOException
85   {
86     StringBuffer treeString = new StringBuffer();
87     String treeName = null;
88     // --------------- Variable Definitions -------------------
89     String line;
90     String version;
91     // String id;
92     Hashtable seqAnn = new Hashtable(); // Sequence related annotations
93     Hashtable seqs = new Hashtable();
94     Regex p, r, rend, s, x;
95
96     // Temporary line for processing RNA annotation
97     // String RNAannot = "";
98
99     // ------------------ Parsing File ----------------------
100     // First, we have to check that this file has STOCKHOLM format, i.e. the
101     // first line must match
102     r = new Regex("# STOCKHOLM ([\\d\\.]+)");
103     if (!r.search(nextLine()))
104     {
105       throw new IOException(
106               "This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM'");
107     }
108     else
109     {
110       version = r.stringMatched(1);
111       // logger.debug("Stockholm version: " + version);
112     }
113
114     // We define some Regexes here that will be used regularily later
115     rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment
116     p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
117     // id/from/to
118     s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype
119     r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line
120     x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence
121
122     // Convert all bracket types to parentheses (necessary for passing to VARNA)
123     Regex openparen = new Regex("(<|\\[)", "(");
124     Regex closeparen = new Regex("(>|\\])", ")");
125
126     // Detect if file is RNA by looking for bracket types
127     Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
128
129     rend.optimize();
130     p.optimize();
131     s.optimize();
132     r.optimize();
133     x.optimize();
134     openparen.optimize();
135     closeparen.optimize();
136
137     while ((line = nextLine()) != null)
138     {
139       if (line.length() == 0)
140       {
141         continue;
142       }
143       if (rend.search(line))
144       {
145         // End of the alignment, pass stuff back
146
147         this.noSeqs = seqs.size();
148         // logger.debug("Number of sequences: " + this.noSeqs);
149         Enumeration accs = seqs.keys();
150         while (accs.hasMoreElements())
151         {
152           String acc = (String) accs.nextElement();
153           // logger.debug("Processing sequence " + acc);
154           String seq = (String) seqs.remove(acc);
155           if (maxLength < seq.length())
156           {
157             maxLength = seq.length();
158           }
159           int start = 1;
160           int end = -1;
161           String sid = acc;
162           /*
163            * Retrieve hash of annotations for this accession Associate
164            * Annotation with accession
165            */
166           Hashtable accAnnotations = null;
167
168           if (seqAnn != null && seqAnn.containsKey(acc))
169           {
170             accAnnotations = (Hashtable) seqAnn.remove(acc);
171             // TODO: add structures to sequence
172           }
173
174           // Split accession in id and from/to
175           if (p.search(acc))
176           {
177             sid = p.stringMatched(1);
178             start = Integer.parseInt(p.stringMatched(2));
179             end = Integer.parseInt(p.stringMatched(3));
180           }
181           // logger.debug(sid + ", " + start + ", " + end);
182
183           Sequence seqO = new Sequence(sid, seq, start, end);
184           // Add Description (if any)
185           if (accAnnotations != null && accAnnotations.containsKey("DE"))
186           {
187             String desc = (String) accAnnotations.get("DE");
188             seqO.setDescription((desc == null) ? "" : desc);
189           }
190           // Add DB References (if any)
191           if (accAnnotations != null && accAnnotations.containsKey("DR"))
192           {
193             String dbr = (String) accAnnotations.get("DR");
194             if (dbr != null && dbr.indexOf(";") > -1)
195             {
196               String src = dbr.substring(0, dbr.indexOf(";"));
197               String acn = dbr.substring(dbr.indexOf(";") + 1);
198               jalview.util.DBRefUtils.parseToDbRef(seqO, src, "0", acn);
199               // seqO.addDBRef(dbref);
200             }
201           }  
202
203           Hashtable features = null;
204           // We need to adjust the positions of all features to account for gaps
205           try
206           {
207             features = (Hashtable) accAnnotations.remove("features");
208           } catch (java.lang.NullPointerException e)
209           {
210             // loggerwarn("Getting Features for " + acc + ": " +
211             // e.getMessage());
212             // continue;
213           }
214           // if we have features
215           if (features != null)
216           {
217             int posmap[] = seqO.findPositionMap();
218             Enumeration i = features.keys();
219             while (i.hasMoreElements())
220             {
221               // TODO: parse out secondary structure annotation as annotation
222               // row
223               // TODO: parse out scores as annotation row
224               // TODO: map coding region to core jalview feature types
225               String type = i.nextElement().toString();
226               Hashtable content = (Hashtable) features.remove(type);
227              
228               // add alignment annotation for this feature
229               String key = type2id(type);
230               if (key != null) 
231               {
232                 if (accAnnotations != null && accAnnotations.containsKey(key))
233                 {
234                   Vector vv = (Vector) accAnnotations.get(key); 
235                   for (int ii = 0; ii < vv.size(); ii++)
236                   {
237                     AlignmentAnnotation an = (AlignmentAnnotation) vv.elementAt(ii);
238                     seqO.addAlignmentAnnotation(an);            
239                   }        
240                 }
241               }
242               
243               Enumeration j = content.keys();
244               while (j.hasMoreElements())
245               {
246                 String desc = j.nextElement().toString();
247                 String ns = content.get(desc).toString();
248                 char[] byChar = ns.toCharArray();
249                 for (int k = 0; k < byChar.length; k++)
250                 {
251                   char c = byChar[k];
252                   if (!(c == ' ' || c == '_' || c == '-' || c == '.')) // PFAM
253                   // uses
254                   // '.'
255                   // for
256                   // feature
257                   // background
258                   {
259                     int new_pos = posmap[k]; // look up nearest seqeunce
260                     // position to this column
261                     SequenceFeature feat = new SequenceFeature(type, desc,
262                             new_pos, new_pos, 0f, null);
263
264                     seqO.addSequenceFeature(feat);
265                   }
266                 }
267               }
268
269             }
270
271           }
272           // garbage collect
273
274           // logger.debug("Adding seq " + acc + " from " + start + " to " + end
275           // + ": " + seq);
276           this.seqs.addElement(seqO);
277         }
278         return; // finished parsing this segment of source
279       }
280       else if (!r.search(line))
281       {
282         // System.err.println("Found sequence line: " + line);
283
284         // Split sequence in sequence and accession parts
285         if (!x.search(line))
286         {
287           // logger.error("Could not parse sequence line: " + line);
288           throw new IOException("Could not parse sequence line: " + line);
289         }
290         String ns = (String) seqs.get(x.stringMatched(1));
291         if (ns == null)
292         {
293           ns = "";
294         }
295         ns += x.stringMatched(2);
296
297         seqs.put(x.stringMatched(1), ns);
298       }
299       else
300       {
301         String annType = r.stringMatched(1);
302         String annContent = r.stringMatched(2);
303
304         // System.err.println("type:" + annType + " content: " + annContent);
305
306         if (annType.equals("GF"))
307         {
308           /*
309            * Generic per-File annotation, free text Magic features: #=GF NH
310            * <tree in New Hampshire eXtended format> #=GF TN <Unique identifier
311            * for the next tree> Pfam descriptions: 7. DESCRIPTION OF FIELDS
312            * 
313            * Compulsory fields: ------------------
314            * 
315            * AC Accession number: Accession number in form PFxxxxx.version or
316            * PBxxxxxx. ID Identification: One word name for family. DE
317            * Definition: Short description of family. AU Author: Authors of the
318            * entry. SE Source of seed: The source suggesting the seed members
319            * belong to one family. GA Gathering method: Search threshold to
320            * build the full alignment. TC Trusted Cutoff: Lowest sequence score
321            * and domain score of match in the full alignment. NC Noise Cutoff:
322            * Highest sequence score and domain score of match not in full
323            * alignment. TP Type: Type of family -- presently Family, Domain,
324            * Motif or Repeat. SQ Sequence: Number of sequences in alignment. AM
325            * Alignment Method The order ls and fs hits are aligned to the model
326            * to build the full align. // End of alignment.
327            * 
328            * Optional fields: ----------------
329            * 
330            * DC Database Comment: Comment about database reference. DR Database
331            * Reference: Reference to external database. RC Reference Comment:
332            * Comment about literature reference. RN Reference Number: Reference
333            * Number. RM Reference Medline: Eight digit medline UI number. RT
334            * Reference Title: Reference Title. RA Reference Author: Reference
335            * Author RL Reference Location: Journal location. PI Previous
336            * identifier: Record of all previous ID lines. KW Keywords: Keywords.
337            * CC Comment: Comments. NE Pfam accession: Indicates a nested domain.
338            * NL Location: Location of nested domains - sequence ID, start and
339            * end of insert.
340            * 
341            * Obsolete fields: ----------- AL Alignment method of seed: The
342            * method used to align the seed members.
343            */
344           // Let's save the annotations, maybe we'll be able to do something
345           // with them later...
346           Regex an = new Regex("(\\w+)\\s*(.*)");
347           if (an.search(annContent))
348           {
349             if (an.stringMatched(1).equals("NH"))
350             {
351               treeString.append(an.stringMatched(2));
352             }
353             else if (an.stringMatched(1).equals("TN"))
354             {
355               if (treeString.length() > 0)
356               {
357                 if (treeName == null)
358                 {
359                   treeName = "Tree " + (getTreeCount() + 1);
360                 }
361                 addNewickTree(treeName, treeString.toString());
362               }
363               treeName = an.stringMatched(2);
364               treeString = new StringBuffer();
365             }
366             setAlignmentProperty(an.stringMatched(1), an.stringMatched(2));
367           }
368         }
369         else if (annType.equals("GS"))
370         {
371           // Generic per-Sequence annotation, free text
372           /*
373            * Pfam uses these features: Feature Description ---------------------
374            * ----------- AC <accession> ACcession number DE <freetext>
375            * DEscription DR <db>; <accession>; Database Reference OS <organism>
376            * OrganiSm (species) OC <clade> Organism Classification (clade, etc.)
377            * LO <look> Look (Color, etc.)
378            */
379           if (s.search(annContent))
380           {
381             String acc = s.stringMatched(1);
382             String type = s.stringMatched(2);
383             String content = s.stringMatched(3);
384             // TODO: store DR in a vector.
385             // TODO: store AC according to generic file db annotation.
386             Hashtable ann;
387             if (seqAnn.containsKey(acc))
388             {
389               ann = (Hashtable) seqAnn.get(acc);
390             }
391             else
392             {
393               ann = new Hashtable();
394             }
395             ann.put(type, content);
396             seqAnn.put(acc, ann);
397           }
398           else
399           {
400             throw new IOException("Error parsing " + line);
401           }
402         }
403         else if (annType.equals("GC"))
404         {
405           // Generic per-Column annotation, exactly 1 char per column
406           // always need a label.
407           if (x.search(annContent))
408           {
409             // parse out and create alignment annotation directly.
410             parseAnnotationRow(annotations, x.stringMatched(1),
411                     x.stringMatched(2));
412           }
413         }
414         else if (annType.equals("GR"))
415         {
416           // Generic per-Sequence AND per-Column markup, exactly 1 char per
417           // column
418           /*
419            * Feature Description Markup letters ------- -----------
420            * -------------- SS Secondary Structure [HGIEBTSCX] SA Surface
421            * Accessibility [0-9X] (0=0%-10%; ...; 9=90%-100%) TM TransMembrane
422            * [Mio] PP Posterior Probability [0-9*] (0=0.00-0.05; 1=0.05-0.15;
423            * *=0.95-1.00) LI LIgand binding [*] AS Active Site [*] IN INtron (in
424            * or after) [0-2]
425            */
426           if (s.search(annContent))
427           {
428             String acc = s.stringMatched(1);
429             String type = s.stringMatched(2);
430             String seq = new String(s.stringMatched(3));
431             String description = null;
432             // Check for additional information about the current annotation
433             // We use a simple string tokenizer here for speed
434             StringTokenizer sep = new StringTokenizer(seq, " \t");
435             description = sep.nextToken();
436             if (sep.hasMoreTokens())
437             {
438               seq = sep.nextToken();
439             }
440             else
441             {
442               seq = description;
443               description = new String();
444             }
445             // sequence id with from-to fields
446
447             Hashtable ann;
448             // Get an object with all the annotations for this sequence
449             if (seqAnn.containsKey(acc))
450             {
451               // logger.debug("Found annotations for " + acc);
452               ann = (Hashtable) seqAnn.get(acc);
453             }
454             else
455             {
456               // logger.debug("Creating new annotations holder for " + acc);
457               ann = new Hashtable();
458               seqAnn.put(acc, ann);
459             }
460             // TODO test structure, call parseAnnotationRow with vector from
461             // hashtable for specific sequence
462             Hashtable features;
463             // Get an object with all the content for an annotation
464             if (ann.containsKey("features"))
465             {
466               // logger.debug("Found features for " + acc);
467               features = (Hashtable) ann.get("features");
468             }
469             else
470             {
471               // logger.debug("Creating new features holder for " + acc);
472               features = new Hashtable();
473               ann.put("features", features);
474             }
475
476             Hashtable content;
477             if (features.containsKey(this.id2type(type)))
478             {
479               // logger.debug("Found content for " + this.id2type(type));
480               content = (Hashtable) features.get(this.id2type(type));
481             }
482             else
483             {
484               // logger.debug("Creating new content holder for " +
485               // this.id2type(type));
486               content = new Hashtable();
487               features.put(this.id2type(type), content);
488             }
489             String ns = (String) content.get(description);
490             if (ns == null)
491             {
492               ns = "";
493             }
494             ns += seq;
495             content.put(description, ns);
496             Hashtable strucAnn;
497             if (seqAnn.containsKey(acc))
498             {
499               strucAnn = (Hashtable) seqAnn.get(acc);
500             }
501             else
502             {
503               strucAnn = new Hashtable();
504             }
505
506             Vector newStruc = new Vector();
507             parseAnnotationRow(newStruc, type, ns);
508             strucAnn.put(type, newStruc);
509             seqAnn.put(acc, strucAnn);
510           }
511           else
512           {
513             System.err
514                     .println("Warning - couldn't parse sequence annotation row line:\n"
515                             + line);
516             // throw new IOException("Error parsing " + line);
517           }
518         }
519         else
520         {
521           throw new IOException("Unknown annotation detected: " + annType
522                   + " " + annContent);
523         }
524       }
525     }
526     if (treeString.length() > 0)
527     {
528       if (treeName == null)
529       {
530         treeName = "Tree " + (1 + getTreeCount());
531       }
532       addNewickTree(treeName, treeString.toString());
533     }
534   }
535
536   protected static AlignmentAnnotation parseAnnotationRow(
537           Vector annotation, String label, String annots)
538   {
539     String convert1, convert2 = null;
540
541     // Convert all bracket types to parentheses
542     Regex openparen = new Regex("(<|\\[)", "(");
543     Regex closeparen = new Regex("(>|\\])", ")");
544
545     // Detect if file is RNA by looking for bracket types
546     Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
547
548     convert1 = openparen.replaceAll(annots);
549     convert2 = closeparen.replaceAll(convert1);
550     annots = convert2;
551
552     String type = (label.indexOf("_cons") == label.length() - 5) ? label
553             .substring(0, label.length() - 5) : label;
554     boolean ss = false;
555     type = id2type(type);
556     if (type.equals("secondary structure"))
557     {
558       ss = true;
559     }
560     // decide on secondary structure or not.
561     Annotation[] els = new Annotation[annots.length()];
562     for (int i = 0; i < annots.length(); i++)
563     {
564       String pos = annots.substring(i, i + 1);
565       Annotation ann;
566       ann = new Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not
567       // be written out
568       if (ss)
569       {
570         if (detectbrackets.search(pos))
571         {
572           ann.secondaryStructure = jalview.schemes.ResidueProperties
573                   .getRNASecStrucState(pos).charAt(0);
574         }
575         else
576         {
577           ann.secondaryStructure = jalview.schemes.ResidueProperties
578                   .getDssp3state(pos).charAt(0);
579         }
580
581         if (ann.secondaryStructure == pos.charAt(0) || pos.charAt(0) == 'C')
582         {
583           ann.displayCharacter = ""; // null; // " ";
584         }
585         else
586         {
587           ann.displayCharacter = " " + ann.displayCharacter;
588         }
589       }
590
591       els[i] = ann;
592     }
593     AlignmentAnnotation annot = null;
594     Enumeration e = annotation.elements();
595     while (e.hasMoreElements())
596     {
597       annot = (AlignmentAnnotation) e.nextElement();
598       if (annot.label.equals(type))
599         break;
600       annot = null;
601     }
602     if (annot == null)
603     {
604       annot = new AlignmentAnnotation(type, type, els);
605       annotation.addElement(annot);
606     }
607     else
608     {
609       Annotation[] anns = new Annotation[annot.annotations.length
610               + els.length];
611       System.arraycopy(annot.annotations, 0, anns, 0,
612               annot.annotations.length);
613       System.arraycopy(els, 0, anns, annot.annotations.length, els.length);
614       annot.annotations = anns;
615       // System.out.println("else: ");
616     }
617     return annot;
618   }
619
620   public String print(SequenceI[] s)
621   {
622           // find max length of id
623             int max = 0;
624             int maxid = 0;
625             int in = 0;
626             while ((in < s.length) && (s[in] != null))
627             {
628               String tmp = printId(s[in]);
629               if (s[in].getSequence().length > max)
630               {
631                 max = s[in].getSequence().length;
632               }
633
634               if (tmp.length() > maxid)
635               {
636                 maxid = tmp.length();
637               }
638
639               in++;
640             }
641             maxid += 9;
642             int i = 0;
643             
644             while (i < s.length && s[i] != null) 
645             {
646               if (s[i].getDatasetSequence() != null) 
647               {
648                 SequenceI ds = s[i].getDatasetSequence();
649                         AlignmentAnnotation[] alAnot;
650                     Annotation[] ann;
651                     Annotation annot;  
652                     alAnot = s[i].getAnnotation();
653                     String feature = "";
654                 if (alAnot != null) 
655                 {
656                           for (int j = 0; j < alAnot.length; j++) 
657                           {     
658                             if (ds.getSequenceFeatures() != null) 
659                     {
660                                   feature = ds.getSequenceFeatures()[0].type;
661                             }   
662                             String key = type2id(feature);
663                             
664                           // output annotations
665                         if (key == null)
666                             continue;
667                         
668                           //  out.append("#=GR ");
669                             out.append(new Format("%-" + maxid + "s").form("#=GR " + printId(s[i]) + " " + key + " "));
670                             ann = alAnot[j].annotations;
671                             String seq = "";
672                                 for (int k = 0; k < ann.length; k++) 
673                                 {         
674                               annot = ann[k]; 
675                               String ch = (annot == null) ? Character.toString(s[i].getCharAt(k)) : annot.displayCharacter;
676                               if (ch.length() == 0)
677                           {
678                             if (key.equals("SS")) {
679                               char ll = annot.secondaryStructure;
680                                   seq = (Character.toString(ll).equals(" ")) ? seq + "C" : seq + ll;    
681                                 } else {
682                                   seq += ".";  
683                                   }
684                                 } else if (ch.length() == 1) {
685                                       seq += ch;
686                                     } else if (ch.length() > 1) {
687                                       seq += ch.charAt(1) ;
688                                 }   
689                               }
690                               out.append(seq);
691                               out.append(newline);
692                           }
693                 }
694                   }
695             
696               out.append(new Format("%-" + maxid + "s").form(printId(s[i])+" "));
697                   out.append(s[i].getSequenceAsString());
698                   out.append(newline);  
699               i++;
700             } 
701             
702             // alignment annotation
703             AlignmentAnnotation aa;
704             if (al.getAlignmentAnnotation() != null) 
705             {
706               for (int ia = 0; ia < al.getAlignmentAnnotation().length; ia++)
707               {
708                 aa = al.getAlignmentAnnotation()[ia];
709                 if (aa.autoCalculated || !aa.visible)
710                 {
711                   continue;
712                 }
713                 String seq = "";
714                 String label = type2id(aa.label);
715
716                 if (label == null) 
717                   label = aa.label;     
718                 
719                 label += "_cons";
720                 
721                 out.append(new Format("%-" + maxid + "s").form("#=GC " + label+" "));
722                 for (int j = 0; j < aa.annotations.length; j++) 
723                 {
724                   String ch = (aa.annotations[j] == null) ? "-" : aa.annotations[j].displayCharacter ;
725                   if (ch.length() == 0) 
726                   {
727                     char ll = aa.annotations[j].secondaryStructure;
728                             if (Character.toString(ll).equals(" "))
729                               seq += "C";
730                                 else 
731                               seq += ll;  
732                   } else if (ch.length() == 1) {
733                     seq += ch;
734                   } else if (ch.length() > 1) {
735                     seq += ch.charAt(1) ;
736                   }   
737                 }
738                     out.append(seq);
739                 out.append(newline);
740               }
741             }
742                 return out.toString();
743   }
744
745   public String print()
746   {
747         out = new StringBuffer();
748         out.append("# STOCKHOLM 1.0");
749         out.append(newline); 
750     print(getSeqsAsArray());
751             
752     out.append("//");
753     out.append(newline);  
754     return out.toString();
755   }
756
757   private static Hashtable typeIds = null;
758   static
759   {
760     if (typeIds == null)
761     {
762       typeIds = new Hashtable();
763       typeIds.put("SS", "secondary structure");
764       typeIds.put("SA", "surface accessibility");
765       typeIds.put("TM", "transmembrane");
766       typeIds.put("PP", "posterior probability");
767       typeIds.put("LI", "ligand binding");
768       typeIds.put("AS", "active site");
769       typeIds.put("IN", "intron");
770       typeIds.put("IR", "interacting residue");
771       typeIds.put("AC", "accession");
772       typeIds.put("OS", "organism");
773       typeIds.put("CL", "class");
774       typeIds.put("DE", "description");
775       typeIds.put("DR", "reference");
776       typeIds.put("LO", "look");
777       typeIds.put("RF", "reference positions");
778
779     }
780   }
781
782   protected static String id2type(String id)
783   {
784     if (typeIds.containsKey(id))
785     {
786       return (String) typeIds.get(id);
787     }
788     System.err.println("Warning : Unknown Stockholm annotation type code "
789             + id);
790     return id;
791   }
792   
793   protected static String type2id(String type)
794   {
795           String key = null;
796           Enumeration e = typeIds.keys();
797       while (e.hasMoreElements()) 
798       {
799         Object ll = e.nextElement();
800         if (typeIds.get(ll).toString().equals(type))
801         {       
802           key = (String) ll;
803           break;
804         }
805       }  
806       if (key != null)
807       {
808          return (String) key;
809       }
810       System.err.println("Warning : Unknown Stockholm annotation type: "
811             + type);
812     return key;
813   }
814   /**
815    * //ssline is complete secondary structure line private AlignmentAnnotation
816    * addHelices(Vector annotation, String label, String ssline) {
817    * 
818    * // decide on secondary structure or not. Annotation[] els = new
819    * Annotation[ssline.length()]; for (int i = 0; i < ssline.length(); i++) {
820    * String pos = ssline.substring(i, i + 1); Annotation ann; ann = new
821    * Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not
822    * 
823    * ann.secondaryStructure =
824    * jalview.schemes.ResidueProperties.getRNAssState(pos).charAt(0);
825    * 
826    * ann.displayCharacter = "x" + ann.displayCharacter;
827    * 
828    * System.out.println(ann.displayCharacter);
829    * 
830    * els[i] = ann; } AlignmentAnnotation helicesAnnot = null; Enumeration e =
831    * annotation.elements(); while (e.hasMoreElements()) { helicesAnnot =
832    * (AlignmentAnnotation) e.nextElement(); if (helicesAnnot.label.equals(type))
833    * break; helicesAnnot = null; } if (helicesAnnot == null) { helicesAnnot =
834    * new AlignmentAnnotation(type, type, els);
835    * annotation.addElement(helicesAnnot); } else { Annotation[] anns = new
836    * Annotation[helicesAnnot.annotations.length + els.length];
837    * System.arraycopy(helicesAnnot.annotations, 0, anns, 0,
838    * helicesAnnot.annotations.length); System.arraycopy(els, 0, anns,
839    * helicesAnnot.annotations.length, els.length); helicesAnnot.annotations =
840    * anns; }
841    * 
842    * helicesAnnot.features = Rna.GetBasePairs(ssline);
843    * Rna.HelixMap(helicesAnnot.features);
844    * 
845    * 
846    * return helicesAnnot; }
847    */
848 }