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