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