update author list in license for (JAL-826)
[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 jalview.datamodel.*;
24 import jalview.util.*;
25
26 /**
27  * DOCUMENT ME!
28  * 
29  * @author $author$
30  * @version $Revision$
31  */
32 public class MSFfile extends AlignFile
33 {
34
35   /**
36    * Creates a new MSFfile object.
37    */
38   public MSFfile()
39   {
40   }
41
42   /**
43    * Creates a new MSFfile object.
44    * 
45    * @param inFile
46    *          DOCUMENT ME!
47    * @param type
48    *          DOCUMENT ME!
49    * 
50    * @throws IOException
51    *           DOCUMENT ME!
52    */
53   public MSFfile(String inFile, String type) throws IOException
54   {
55     super(inFile, type);
56   }
57
58   public MSFfile(FileParse source) throws IOException
59   {
60     super(source);
61   }
62
63   {
64     // TODO Auto-generated constructor stub
65   }
66
67   /**
68    * DOCUMENT ME!
69    */
70   public void parse() 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     } catch (IOException e)
131     {
132       System.err.println("Exception parsing MSFFile " + e);
133       e.printStackTrace();
134     }
135
136     this.noSeqs = headers.size();
137
138     // Add sequences to the hash
139     for (i = 0; i < headers.size(); i++)
140     {
141       if (seqhash.get(headers.elementAt(i)) != null)
142       {
143         String head = headers.elementAt(i).toString();
144         String seq = seqhash.get(head).toString();
145
146         if (maxLength < head.length())
147         {
148           maxLength = head.length();
149         }
150
151         // Replace ~ with a sensible gap character
152         seq = seq.replace('~', '-');
153
154         Sequence newSeq = parseId(head);
155
156         newSeq.setSequence(seq);
157
158         seqs.addElement(newSeq);
159       }
160       else
161       {
162         System.err.println("MSFFile Parser: Can't find sequence for "
163                 + headers.elementAt(i));
164       }
165     }
166   }
167
168   /**
169    * DOCUMENT ME!
170    * 
171    * @param seq
172    *          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       } catch (Exception e)
192       {
193         System.err.println("Exception during MSF Checksum calculation");
194         e.printStackTrace();
195       }
196     }
197
198     return check % 10000;
199   }
200
201   /**
202    * DOCUMENT ME!
203    * 
204    * @param s
205    *          DOCUMENT ME!
206    * @param is_NA
207    *          DOCUMENT ME!
208    * 
209    * @return DOCUMENT ME!
210    */
211   public String print(SequenceI[] seqs)
212   {
213
214     boolean is_NA = jalview.util.Comparison.isNucleotide(seqs);
215
216     SequenceI[] s = new SequenceI[seqs.length];
217
218     StringBuffer out = new StringBuffer("!!" + (is_NA ? "NA" : "AA")
219             + "_MULTIPLE_ALIGNMENT 1.0");
220      // TODO: JBPNote : Jalview doesn't remember NA or AA yet.
221     out.append(newline);
222     out.append(newline);
223     int max = 0;
224     int maxid = 0;
225     int i = 0;
226
227     while ((i < seqs.length) && (seqs[i] != null))
228     {
229       // Replace all internal gaps with . and external spaces with ~
230       s[i] = new Sequence(seqs[i].getName(), seqs[i].getSequenceAsString()
231               .replace('-', '.'), seqs[i].getStart(),seqs[i].getEnd());
232
233       StringBuffer sb = new StringBuffer();
234       sb.append(s[i].getSequence());
235
236       for (int ii = 0; ii < sb.length(); ii++)
237       {
238         if (sb.charAt(ii) == '.')
239         {
240           sb.setCharAt(ii, '~');
241         }
242         else
243         {
244           break;
245         }
246       }
247
248       for (int ii = sb.length() - 1; ii > 0; ii--)
249       {
250         if (sb.charAt(ii) == '.')
251         {
252           sb.setCharAt(ii, '~');
253         }
254         else
255         {
256           break;
257         }
258       }
259
260       s[i].setSequence(sb.toString());
261
262       if (s[i].getSequence().length > max)
263       {
264         max = s[i].getSequence().length;
265       }
266
267       i++;
268     }
269
270     Format maxLenpad = new Format("%" + (new String("" + max)).length()
271             + "d");
272     Format maxChkpad = new Format("%" + (new String("1" + max)).length()
273             + "d");
274     i = 0;
275
276     int bigChecksum = 0;
277     int[] checksums = new int[s.length];
278     while (i < s.length)
279     {
280       checksums[i] = checkSum(s[i].getSequenceAsString());
281       bigChecksum += checksums[i];
282       i++;
283     }
284
285     long maxNB = 0;
286     out.append("   MSF: " + s[0].getSequence().length + "   Type: "
287             + (is_NA ? "N" : "P") + "    Check:  " + (bigChecksum % 10000)
288             + "   ..");
289     out.append(newline);
290     out.append(newline);
291     out.append(newline);
292
293     String[] nameBlock = new String[s.length];
294     String[] idBlock = new String[s.length];
295
296     i = 0;
297     while ((i < s.length) && (s[i] != null))
298     {
299
300       nameBlock[i] = new String("  Name: " + printId(s[i]) + " ");
301
302       idBlock[i] = new String("Len: "
303               + maxLenpad.form(s[i].getSequence().length) + "  Check: "
304               + maxChkpad.form(checksums[i]) + "  Weight: 1.00"+newline);
305
306       if (s[i].getName().length() > maxid)
307       {
308         maxid = s[i].getName().length();
309       }
310
311       if (nameBlock[i].length() > maxNB)
312       {
313         maxNB = nameBlock[i].length();
314       }
315
316       i++;
317     }
318
319     if (maxid < 10)
320     {
321       maxid = 10;
322     }
323
324     if (maxNB < 15)
325     {
326       maxNB = 15;
327     }
328
329     Format nbFormat = new Format("%-" + maxNB + "s");
330
331     for (i = 0; (i < s.length) && (s[i] != null); i++)
332     {
333       out.append(nbFormat.form(nameBlock[i]) + idBlock[i]);
334     }
335
336     maxid++;
337     out.append(newline);
338     out.append(newline);out.append("//");
339     out.append(newline);
340     out.append(newline);
341     int len = 50;
342
343     int nochunks = (max / len) + 1;
344
345     if ((max % len) == 0)
346     {
347       nochunks--;
348     }
349
350     for (i = 0; i < nochunks; i++)
351     {
352       int j = 0;
353
354       while ((j < s.length) && (s[j] != null))
355       {
356         String name = printId(s[j]);
357
358         out.append(new Format("%-" + maxid + "s").form(name + " "));
359
360         for (int k = 0; k < 5; k++)
361         {
362           int start = (i * 50) + (k * 10);
363           int end = start + 10;
364
365           if ((end < s[j].getSequence().length)
366                   && (start < s[j].getSequence().length))
367           {
368             out.append(s[j].getSequence(start, end));
369
370             if (k < 4)
371             {
372               out.append(" ");
373             }
374             else
375             {
376               out.append(newline);
377             }
378           }
379           else
380           {
381             if (start < s[j].getSequence().length)
382             {
383               out.append(s[j].getSequenceAsString().substring(start));
384               out.append(newline);
385             }
386             else
387             {
388               if (k == 0)
389               {
390                 out.append(newline);
391               }
392             }
393           }
394         }
395
396         j++;
397       }
398
399       out.append(newline);
400     }
401
402     return out.toString();
403   }
404
405   /**
406    * DOCUMENT ME!
407    * 
408    * @return DOCUMENT ME!
409    */
410   public String print()
411   {
412     return print(getSeqsAsArray());
413   }
414 }