GPL license added
[jalview.git] / src / jalview / io / MSFfile.java
1 /*\r
2 * Jalview - A Sequence Alignment Editor and Viewer\r
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4 *\r
5 * This program is free software; you can redistribute it and/or\r
6 * modify it under the terms of the GNU General Public License\r
7 * as published by the Free Software Foundation; either version 2\r
8 * of the License, or (at your option) any later version.\r
9 *\r
10 * This program is distributed in the hope that it will be useful,\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 * GNU General Public License for more details.\r
14 *\r
15 * You should have received a copy of the GNU General Public License\r
16 * along with this program; if not, write to the Free Software\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18 */\r
19 \r
20 package jalview.io;\r
21 \r
22 import jalview.datamodel.*;\r
23 import jalview.util.*;\r
24 \r
25 import java.io.*;\r
26 import java.util.*;\r
27 \r
28 public class MSFfile extends AlignFile {\r
29 \r
30   public MSFfile()\r
31   {}\r
32 \r
33   public MSFfile(String inStr) {\r
34     super(inStr);\r
35   }\r
36 \r
37   public MSFfile(String inFile, String type) throws IOException {\r
38     super(inFile,type);\r
39   }\r
40 \r
41   private static com.stevesoft.pat.Regex gapre = new com.stevesoft.pat.Regex("\\~","-");\r
42   private static com.stevesoft.pat.Regex re2gap = new com.stevesoft.pat.Regex("["+jalview.util.Comparison.GapChars+"]","\\~");\r
43 \r
44   public void parse() {\r
45     int       i       = 0;\r
46     boolean   seqFlag = false;\r
47     String    key     = new String();\r
48     Vector    headers = new Vector();\r
49     Hashtable seqhash = new Hashtable();\r
50     String    line;\r
51 \r
52     try {\r
53     while ((line = nextLine()) != null) {\r
54 \r
55       StringTokenizer str = new StringTokenizer(line);\r
56 \r
57       while (str.hasMoreTokens()) {\r
58 \r
59         String inStr = str.nextToken();\r
60 \r
61         //If line has header information add to the headers vector\r
62         if (inStr.indexOf("Name:") != -1) {\r
63           key = str.nextToken();\r
64           headers.addElement(key);\r
65         }\r
66 \r
67         //if line has // set SeqFlag to 1 so we know sequences are coming\r
68         if (inStr.indexOf("//") != -1) {\r
69           seqFlag = true;\r
70         }\r
71 \r
72         //Process lines as sequence lines if seqFlag is set\r
73         if (( inStr.indexOf("//") == -1) && (seqFlag == true)) {\r
74           //seqeunce id is the first field\r
75           key = inStr;\r
76           StringBuffer tempseq;\r
77 \r
78           //Get sequence from hash if it exists\r
79           if (seqhash.containsKey(key)) {\r
80             tempseq = (StringBuffer)seqhash.get(key);\r
81           } else {\r
82             tempseq = new StringBuffer();\r
83             seqhash.put(key,tempseq);\r
84           }\r
85 \r
86           //loop through the rest of the words\r
87           while (str.hasMoreTokens()) {\r
88             //append the word to the sequence\r
89             tempseq.append(str.nextToken());\r
90           }\r
91         }\r
92       }\r
93     }\r
94     } catch (IOException e) {\r
95       System.err.println("Exception parsing MSFFile " + e);\r
96       e.printStackTrace();\r
97     }\r
98 \r
99     this.noSeqs = headers.size();\r
100 \r
101     //Add sequences to the hash\r
102     for (i = 0; i < headers.size(); i++ ) {\r
103 \r
104       if ( seqhash.get(headers.elementAt(i)) != null) {\r
105         String head =  headers.elementAt(i).toString();\r
106         String seq  =  seqhash.get(head).toString();\r
107 \r
108         int start = 1;\r
109         int end = seq.length();\r
110 \r
111         if (maxLength <  head.length() ) {\r
112           maxLength =  head.length();\r
113         }\r
114 \r
115         if (head.indexOf("/") > 0 ) {\r
116 \r
117           StringTokenizer st = new StringTokenizer(head,"/");\r
118 \r
119           if (st.countTokens() == 2) {\r
120 \r
121             head = st.nextToken();\r
122             String tmp = st.nextToken();\r
123             st = new StringTokenizer(tmp,"-");\r
124             if (st.countTokens() == 2) {\r
125               start = Integer.valueOf(st.nextToken()).intValue();\r
126               end = Integer.valueOf(st.nextToken()).intValue();\r
127             }\r
128           }\r
129         }\r
130         // Replace ~ with a sensible gap character\r
131         seq = gapre.replaceAll(seq);\r
132         Sequence newSeq = new Sequence(head,seq,start,end);\r
133 \r
134         seqs.addElement(newSeq);\r
135 \r
136       } else {\r
137         System.err.println("MSFFile Parser: Can't find sequence for " + headers.elementAt(i));\r
138       }\r
139     }\r
140 \r
141   }\r
142 \r
143   public static int checkSum(String seq) {\r
144     //String chars =  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.*~&@";\r
145     int check = 0;\r
146 \r
147     String index =  "--------------------------------------&---*---.-----------------@ABCDEFGHIJKLMNOPQRSTUVWXYZ------ABCDEFGHIJKLMNOPQRSTUVWXYZ----@";\r
148     index += "--------------------------------------------------------------------------------------------------------------------------------";\r
149 \r
150     for(int i = 0; i < seq.length(); i++) {\r
151       try {\r
152         if (i <seq.length()) {\r
153           int pos = index.indexOf(seq.substring(i,i+1));\r
154           if (!index.substring(pos,pos+1).equals("_")) {\r
155             check += ((i % 57) + 1) * pos;\r
156           }\r
157         }\r
158       } catch (Exception e) {\r
159         System.err.println("Exception during MSF Checksum calculation");\r
160         e.printStackTrace();\r
161       }\r
162     }\r
163     return check % 10000;\r
164   }\r
165 \r
166   public static String print(SequenceI[] s) {\r
167     return print(s, false);\r
168   }\r
169   public static String print(SequenceI[] s, boolean is_NA) {\r
170     StringBuffer out = new StringBuffer("!!"+(is_NA ? "NA":"AA")+"_MULTIPLE_ALIGNMENT 1.0\n\n"); // TODO: JBPNote : Jalview doesn't remember NA or AA yet.\r
171 \r
172     int max = 0;\r
173     int maxid = 0;\r
174     int i = 0;\r
175     String big = "";\r
176 \r
177     while (i < s.length && s[i] != null) {\r
178       String sq;\r
179       big += (sq=s[i].getSequence());\r
180       if (sq.length() > max) {\r
181         max = sq.length();\r
182       }\r
183       i++;\r
184     }\r
185     Format maxLenpad = new Format("%"+(new String(""+max)).length()+"d");\r
186     Format maxChkpad = new Format("%"+(new String("1"+max)).length()+"d");\r
187     i = 0;\r
188     long bigcheck = checkSum(big);\r
189     long maxNB=0;\r
190     out.append("   MSF: " + s[0].getSequence().length() + "   Type: "+(is_NA?"N":"P")+"    Check:  " + bigcheck + "   ..\n\n\n");\r
191     String nameBlock[] = new String[s.length];\r
192     String idBlock[] = new String[s.length];\r
193     while (i < s.length && s[i] != null) {\r
194       String seq = s[i].getSequence();\r
195       String name =  s[i].getName()+ "/" + s[i].getStart() + "-" + s[i].getEnd();\r
196       int check = checkSum(s[i].getSequence());\r
197       nameBlock[i]=new String("  Name: "+name+" ");\r
198       idBlock[i] = new String("Len: " + maxLenpad.form(s[i].getSequence().length()) + "  Check:" + maxChkpad.form(check) + "  Weight: 1.00\n");\r
199 \r
200       if (name.length() > maxid) {\r
201         maxid = name.length();\r
202       }\r
203       if (nameBlock[i].length()>maxNB) {\r
204         maxNB=nameBlock[i].length();\r
205       }\r
206 \r
207       i++;\r
208     }\r
209     if (maxid < 10) {\r
210       maxid = 10;\r
211     }\r
212     if (maxNB<15) {\r
213       maxNB=15;\r
214     }\r
215     Format nbFormat = new Format("%-"+maxNB+"s");\r
216     for (i=0;i<s.length && s[i]!=null;i++) {\r
217       out.append(nbFormat.form(nameBlock[i])+idBlock[i]);\r
218     }\r
219     maxid++;\r
220     out.append( "\n\n//\n\n");\r
221 \r
222     int len = 50;\r
223 \r
224     int nochunks =  max / len + 1;\r
225     if (max%len == 0) {\r
226       nochunks--;\r
227     }\r
228     for (i = 0; i < nochunks; i++) {\r
229       int j = 0;\r
230       while (j < s.length && s[j] != null) {\r
231         String name =  s[j].getName();\r
232         out.append( new Format("%-" + maxid + "s").form(name + "/" + s[j].getStart() + "-" + s[j].getEnd()) + " ");\r
233         for (int k = 0; k < 5; k++) {\r
234 \r
235           int start = i*50 + k*10;\r
236           int end = start + 10;\r
237 \r
238           if (end < s[j].getSequence().length() && start < s[j].getSequence().length() ) {\r
239             out.append(re2gap.replaceAll(s[j].getSequence().substring(start,end)));\r
240             if (k < 4) {\r
241               // out.append(" ");\r
242             } else {\r
243               out.append("\n");\r
244             }\r
245           } else {\r
246             if (start < s[j].getSequence().length()) {\r
247               out.append(re2gap.replaceAll(s[j].getSequence().substring(start)));\r
248               out.append("\n");\r
249             } else {\r
250               if (k == 0) {\r
251                 out.append("\n");\r
252               }\r
253             }\r
254           }\r
255         }\r
256         j++;\r
257       }\r
258       out.append("\n");\r
259 \r
260     }\r
261     return out.toString();\r
262   }\r
263   public String print() {\r
264     return print(getSeqsAsArray());\r
265   }\r
266 }\r
267 \r
268 \r
269 \r
270 \r
271 \r
272 \r
273 \r