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