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