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