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