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