Add support RNAML format
[jalview.git] / src / jalview / io / MSFfile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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 package jalview.io;
19
20 import java.io.*;
21 import java.util.*;
22
23 import javax.xml.parsers.ParserConfigurationException;
24
25 import org.xml.sax.SAXException;
26
27 import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
28 import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
29 import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;
30
31 import jalview.datamodel.*;
32 import jalview.util.*;
33
34 /**
35  * DOCUMENT ME!
36  * 
37  * @author $author$
38  * @version $Revision$
39  */
40 public class MSFfile extends AlignFile
41 {
42
43   /**
44    * Creates a new MSFfile object.
45    */
46   public MSFfile()
47   {
48   }
49
50   /**
51    * Creates a new MSFfile object.
52    * 
53    * @param inFile
54    *          DOCUMENT ME!
55    * @param type
56    *          DOCUMENT ME!
57    * 
58    * @throws IOException
59    *           DOCUMENT ME!
60  * @throws SAXException 
61  * @throws ParserConfigurationException 
62  * @throws ExceptionFileFormatOrSyntax 
63  * @throws ExceptionLoadingFailed 
64  * @throws ExceptionPermissionDenied 
65    */
66   public MSFfile(String inFile, String type) throws IOException, ExceptionFileFormatOrSyntax, ParserConfigurationException, SAXException, ExceptionPermissionDenied, ExceptionLoadingFailed
67   {
68     super(inFile, type);
69   }
70
71   public MSFfile(FileParse source) throws IOException, ExceptionFileFormatOrSyntax, ParserConfigurationException, SAXException, ExceptionPermissionDenied, ExceptionLoadingFailed
72   {
73     super(source);
74   }
75
76   {
77     // TODO Auto-generated constructor stub
78   }
79
80   /**
81    * DOCUMENT ME!
82    */
83   public void parse() throws IOException
84   {
85     int i = 0;
86     boolean seqFlag = false;
87     String key = new String();
88     Vector headers = new Vector();
89     Hashtable seqhash = new Hashtable();
90     String line;
91
92     try
93     {
94       while ((line = nextLine()) != null)
95       {
96         StringTokenizer str = new StringTokenizer(line);
97
98         while (str.hasMoreTokens())
99         {
100           String inStr = str.nextToken();
101
102           // If line has header information add to the headers vector
103           if (inStr.indexOf("Name:") != -1)
104           {
105             key = str.nextToken();
106             headers.addElement(key);
107           }
108
109           // if line has // set SeqFlag to 1 so we know sequences are coming
110           if (inStr.indexOf("//") != -1)
111           {
112             seqFlag = true;
113           }
114
115           // Process lines as sequence lines if seqFlag is set
116           if ((inStr.indexOf("//") == -1) && (seqFlag == true))
117           {
118             // seqeunce id is the first field
119             key = inStr;
120
121             StringBuffer tempseq;
122
123             // Get sequence from hash if it exists
124             if (seqhash.containsKey(key))
125             {
126               tempseq = (StringBuffer) seqhash.get(key);
127             }
128             else
129             {
130               tempseq = new StringBuffer();
131               seqhash.put(key, tempseq);
132             }
133
134             // loop through the rest of the words
135             while (str.hasMoreTokens())
136             {
137               // append the word to the sequence
138               tempseq.append(str.nextToken());
139             }
140           }
141         }
142       }
143     } catch (IOException e)
144     {
145       System.err.println("Exception parsing MSFFile " + e);
146       e.printStackTrace();
147     }
148
149     this.noSeqs = headers.size();
150
151     // Add sequences to the hash
152     for (i = 0; i < headers.size(); i++)
153     {
154       if (seqhash.get(headers.elementAt(i)) != null)
155       {
156         String head = headers.elementAt(i).toString();
157         String seq = seqhash.get(head).toString();
158
159         if (maxLength < head.length())
160         {
161           maxLength = head.length();
162         }
163
164         // Replace ~ with a sensible gap character
165         seq = seq.replace('~', '-');
166
167         Sequence newSeq = parseId(head);
168
169         newSeq.setSequence(seq);
170
171         seqs.addElement(newSeq);
172       }
173       else
174       {
175         System.err.println("MSFFile Parser: Can't find sequence for "
176                 + headers.elementAt(i));
177       }
178     }
179   }
180
181   /**
182    * DOCUMENT ME!
183    * 
184    * @param seq
185    *          DOCUMENT ME!
186    * 
187    * @return DOCUMENT ME!
188    */
189   public int checkSum(String seq)
190   {
191     int check = 0;
192     String sequence = seq.toUpperCase();
193
194     for (int i = 0; i < sequence.length(); i++)
195     {
196       try
197       {
198
199         int value = sequence.charAt(i);
200         if (value != -1)
201         {
202           check += (i % 57 + 1) * value;
203         }
204       } catch (Exception e)
205       {
206         System.err.println("Exception during MSF Checksum calculation");
207         e.printStackTrace();
208       }
209     }
210
211     return check % 10000;
212   }
213
214   /**
215    * DOCUMENT ME!
216    * 
217    * @param s
218    *          DOCUMENT ME!
219    * @param is_NA
220    *          DOCUMENT ME!
221    * 
222    * @return DOCUMENT ME!
223    */
224   public String print(SequenceI[] seqs)
225   {
226
227     boolean is_NA = jalview.util.Comparison.isNucleotide(seqs);
228
229     SequenceI[] s = new SequenceI[seqs.length];
230
231     StringBuffer out = new StringBuffer("!!" + (is_NA ? "NA" : "AA")
232             + "_MULTIPLE_ALIGNMENT 1.0");
233      // TODO: JBPNote : Jalview doesn't remember NA or AA yet.
234     out.append(newline);
235     out.append(newline);
236     int max = 0;
237     int maxid = 0;
238     int i = 0;
239
240     while ((i < seqs.length) && (seqs[i] != null))
241     {
242       // Replace all internal gaps with . and external spaces with ~
243       s[i] = new Sequence(seqs[i].getName(), seqs[i].getSequenceAsString()
244               .replace('-', '.'), seqs[i].getStart(),seqs[i].getEnd());
245
246       StringBuffer sb = new StringBuffer();
247       sb.append(s[i].getSequence());
248
249       for (int ii = 0; ii < sb.length(); ii++)
250       {
251         if (sb.charAt(ii) == '.')
252         {
253           sb.setCharAt(ii, '~');
254         }
255         else
256         {
257           break;
258         }
259       }
260
261       for (int ii = sb.length() - 1; ii > 0; ii--)
262       {
263         if (sb.charAt(ii) == '.')
264         {
265           sb.setCharAt(ii, '~');
266         }
267         else
268         {
269           break;
270         }
271       }
272
273       s[i].setSequence(sb.toString());
274
275       if (s[i].getSequence().length > max)
276       {
277         max = s[i].getSequence().length;
278       }
279
280       i++;
281     }
282
283     Format maxLenpad = new Format("%" + (new String("" + max)).length()
284             + "d");
285     Format maxChkpad = new Format("%" + (new String("1" + max)).length()
286             + "d");
287     i = 0;
288
289     int bigChecksum = 0;
290     int[] checksums = new int[s.length];
291     while (i < s.length)
292     {
293       checksums[i] = checkSum(s[i].getSequenceAsString());
294       bigChecksum += checksums[i];
295       i++;
296     }
297
298     long maxNB = 0;
299     out.append("   MSF: " + s[0].getSequence().length + "   Type: "
300             + (is_NA ? "N" : "P") + "    Check:  " + (bigChecksum % 10000)
301             + "   ..");
302     out.append(newline);
303     out.append(newline);
304     out.append(newline);
305
306     String[] nameBlock = new String[s.length];
307     String[] idBlock = new String[s.length];
308
309     i = 0;
310     while ((i < s.length) && (s[i] != null))
311     {
312
313       nameBlock[i] = new String("  Name: " + printId(s[i]) + " ");
314
315       idBlock[i] = new String("Len: "
316               + maxLenpad.form(s[i].getSequence().length) + "  Check: "
317               + maxChkpad.form(checksums[i]) + "  Weight: 1.00"+newline);
318
319       if (s[i].getName().length() > maxid)
320       {
321         maxid = s[i].getName().length();
322       }
323
324       if (nameBlock[i].length() > maxNB)
325       {
326         maxNB = nameBlock[i].length();
327       }
328
329       i++;
330     }
331
332     if (maxid < 10)
333     {
334       maxid = 10;
335     }
336
337     if (maxNB < 15)
338     {
339       maxNB = 15;
340     }
341
342     Format nbFormat = new Format("%-" + maxNB + "s");
343
344     for (i = 0; (i < s.length) && (s[i] != null); i++)
345     {
346       out.append(nbFormat.form(nameBlock[i]) + idBlock[i]);
347     }
348
349     maxid++;
350     out.append(newline);
351     out.append(newline);out.append("//");
352     out.append(newline);
353     out.append(newline);
354     int len = 50;
355
356     int nochunks = (max / len) + 1;
357
358     if ((max % len) == 0)
359     {
360       nochunks--;
361     }
362
363     for (i = 0; i < nochunks; i++)
364     {
365       int j = 0;
366
367       while ((j < s.length) && (s[j] != null))
368       {
369         String name = printId(s[j]);
370
371         out.append(new Format("%-" + maxid + "s").form(name + " "));
372
373         for (int k = 0; k < 5; k++)
374         {
375           int start = (i * 50) + (k * 10);
376           int end = start + 10;
377
378           if ((end < s[j].getSequence().length)
379                   && (start < s[j].getSequence().length))
380           {
381             out.append(s[j].getSequence(start, end));
382
383             if (k < 4)
384             {
385               out.append(" ");
386             }
387             else
388             {
389               out.append(newline);
390             }
391           }
392           else
393           {
394             if (start < s[j].getSequence().length)
395             {
396               out.append(s[j].getSequenceAsString().substring(start));
397               out.append(newline);
398             }
399             else
400             {
401               if (k == 0)
402               {
403                 out.append(newline);
404               }
405             }
406           }
407         }
408
409         j++;
410       }
411
412       out.append(newline);
413     }
414
415     return out.toString();
416   }
417
418   /**
419    * DOCUMENT ME!
420    * 
421    * @return DOCUMENT ME!
422    */
423   public String print()
424   {
425     return print(getSeqsAsArray());
426   }
427 }