fully integrate HMMER3 file format into Jalview
[jalview.git] / src / jalview / io / HMMFile.java
1 package jalview.io;
2
3 import jalview.datamodel.HMMNode;
4 import jalview.datamodel.HiddenMarkovModel;
5 import jalview.datamodel.SequenceI;
6
7 import java.io.BufferedReader;
8 import java.io.FileNotFoundException;
9 import java.io.IOException;
10 import java.io.PrintWriter;
11 import java.io.UnsupportedEncodingException;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Scanner;
15
16
17 /**
18  * reads in and writes out a HMMER standard file
19  * 
20  * 
21  * @author TZVanaalten
22  *
23  */
24 public class HMMFile extends AlignFile
25         implements AlignmentFileReaderI, AlignmentFileWriterI
26 {
27   // HMM to store file data
28   private HiddenMarkovModel hmm = new HiddenMarkovModel();
29
30
31
32
33   // number of possible transitions
34   private final int NUMBER_OF_TRANSITIONS = 7;
35
36   private final String NEW_LINE = "\n";
37
38
39   // file header
40   String fileHeader;
41
42   int numberOfSymbols;
43
44   private final String SPACE = " ";
45
46   private final String COMPO = "COMPO";
47
48   private final String EMPTY = "";
49
50   public HMMFile(FileParse source) throws IOException
51   {
52     super(false, source);
53   }
54
55   public HMMFile()
56   {
57
58   }
59
60   public HiddenMarkovModel getHMM()
61   {
62     return hmm;
63   }
64
65   public void setHMM(HiddenMarkovModel model)
66   {
67     this.hmm = model;
68   }
69
70   public String getName()
71   {
72     return hmm.getName();
73   }
74
75   /**
76    * reads data from HMM file
77    * 
78    * @throws IOException
79    */
80   @Override
81   public void parse() throws IOException
82   {
83     parseFileProperties(dataIn);
84     parseModel(dataIn);
85   }
86
87
88
89   /**
90    * imports file properties from hmm file
91    * 
92    * @param input
93    *          buffered reader used to read in file
94    * @throws IOException
95    */
96   void parseFileProperties(BufferedReader input) throws IOException
97   {
98     boolean readingFile = true;
99     fileHeader = input.readLine();
100     String line = input.readLine();
101     while (readingFile)
102     {
103       if (line != null)
104       {
105         Scanner parser = new Scanner(line);
106         String next = parser.next();
107         if ("HMM".equals(next)) // indicates start of HMM data (end of file
108                               // properties)
109         {
110           readingFile = false;
111           hmm.fillSymbols(parser);
112           numberOfSymbols = hmm.getNumberOfSymbols();
113         }
114         else if ("STATS".equals(next))
115         {
116           parser.next();
117           String key;
118           String value;
119           key = parser.next();
120           value = parser.next() + SPACE + SPACE + parser.next();
121           hmm.addFileProperty(key, value);
122         }
123         else
124         {
125           String key = next;
126           String value = parser.next();
127           while (parser.hasNext())
128           {
129             value = value + SPACE + parser.next();
130           }
131           hmm.addFileProperty(key, value);
132         }
133         parser.close();
134       }
135       line = input.readLine();
136       if (line == null)
137       {
138         readingFile = false;
139       }
140     }
141
142   }
143
144   /**
145    * parses the model data from the hmm file
146    * 
147    * @param input
148    *          buffered reader used to read file
149    * @throws IOException
150    */
151   void parseModel(BufferedReader input) throws IOException
152   {
153     for (int i = 0; i < hmm.getLength() + 1; i++)
154     {
155       hmm.getNodes().add(new HMMNode());
156       String next;
157       String line;
158       line = input.readLine();
159       Scanner matchReader = new Scanner(line);
160       next = matchReader.next();
161       if (next.equals(COMPO) || i > 0)
162       {
163         // stores match emission line in list
164         List<Double> matches = new ArrayList<>();
165         matches = fillList(matchReader, numberOfSymbols);
166         hmm.getNodes().get(i).setMatchEmissions(matches);
167         if (i > 0)
168         {
169           parseAnnotations(matchReader, i);
170         }
171       }
172       matchReader.close();
173       // stores insert emission line in list
174       line = input.readLine();
175       Scanner insertReader = new Scanner(line);
176       List<Double> inserts = new ArrayList<>();
177       inserts = fillList(insertReader, numberOfSymbols);
178       hmm.getNodes().get(i).setInsertEmissions(inserts);
179       insertReader.close();
180
181       // stores state transition line in list
182       line = input.readLine();
183       Scanner transitionReader = new Scanner(line);
184       List<Double> transitions = new ArrayList<>();
185       transitions = fillList(transitionReader, NUMBER_OF_TRANSITIONS);
186       hmm.getNodes().get(i).setStateTransitions(transitions);
187       transitionReader.close();
188     }
189
190   }
191
192   /**
193    * parses annotations on match emission line
194    * 
195    * @param scanner
196    *          scanner which is processing match emission line
197    * @param index
198    *          index of node which is beign scanned
199    */
200   void parseAnnotations(Scanner scanner, int index)
201   {
202     if (hmm.mapIsActive())
203     {
204       int column;
205       column = scanner.nextInt();
206       hmm.getNodes().get(index).setAlignmentColumn(column);
207       hmm.getNodeLookup().put(column, index);
208     }
209     else
210     {
211       scanner.next();
212     }
213
214     char consensusR;
215     consensusR = charValue(scanner.next());
216     hmm.getNodes().get(index).setConsensusResidue(consensusR);
217
218       char reference;
219       reference = charValue(scanner.next());
220       hmm.getNodes().get(index).setReferenceAnnotation(reference);
221
222
223       char value;
224       value = charValue(scanner.next());
225       hmm.getNodes().get(index).setMaskValue(value);
226
227     char consensusS;
228     consensusS = charValue(scanner.next());
229     hmm.getNodes().get(index).setConsensusStructure(consensusS);
230   }
231
232
233   /**
234    * 
235    * @param input
236    *          scanner for line containing data to be transferred to list
237    * @param numberOfElements
238    *          number of elements in the list to be filled
239    * @return filled list
240    */
241   static List<Double> fillList(Scanner input,
242           int numberOfElements)
243   {
244     List<Double> list = new ArrayList<>();
245     for (int i = 0; i < numberOfElements; i++)
246     {
247
248       String next = input.next();
249       if (next.contains("*")) // state transitions to or from delete states
250                               // occasionally have values of -infinity. These
251                               // values are represented by an * in the .hmm
252                               // file, and by a null value in the
253                               // HiddenMarkovModel class
254       {
255         list.add(Double.NEGATIVE_INFINITY);
256       }
257       else
258       {
259         list.add(Double.valueOf(next));
260       }
261     }
262     return list;
263   }
264
265   
266   /**
267    * writes a HiddenMarkovModel to a file
268    * 
269    * @param exportLocation
270    *          Filename, URL or Pasted String to write to
271    * @throws FileNotFoundException
272    * @throws UnsupportedEncodingException
273    *
274    **/
275   
276   public void exportFile(String exportLocation) throws IOException
277   {
278     StringBuilder file = new StringBuilder();
279     appendFileProperties(file);
280     appendModel(file);
281     
282     file.append("//");
283
284     PrintWriter output = new PrintWriter(exportLocation);
285     output.append(file);
286     output.close();
287
288   }
289
290   String addData(int initialColumnSeparation,
291           int columnSeparation, List<String> data)
292   {
293     String line = EMPTY;
294     int index = 0;
295     for (String value : data)
296     {
297       if (index == 0)
298       {
299         line += String.format("%" + initialColumnSeparation + "s", value);
300       }
301       else
302       {
303         line += String.format("%" + columnSeparation + "s", value);
304       }
305       index++;
306     }
307     return line;
308   }
309
310   List<String> charListToStringList(List<Character> list)
311   {
312     List<String> strList = new ArrayList<>();
313     for (char value : list)
314     {
315       String strValue = Character.toString(value);
316       strList.add(strValue);
317     }
318     return strList;
319   }
320
321   List<String> doubleListToStringList(List<Double> list,
322           int noOfDecimals)
323   {
324     List<String> strList = new ArrayList<>();
325     for (double value : list)
326     {
327       String strValue;
328       if (value == Double.NEGATIVE_INFINITY)
329       {
330         strValue = "*";
331       }
332       else
333       {
334         strValue = String.format("%.5f", value);
335       }
336
337       strList.add(strValue);
338     }
339     return strList;
340   }
341
342   List<String> stringArrayToStringList(String[] array)
343   {
344     List<String> list = new ArrayList<>();
345     for (String value : array)
346     {
347       list.add(value);
348     }
349
350     return list;
351   }
352
353   void appendModel(StringBuilder file)
354   {
355     String symbolLine = "HMM";
356     List<Character> charSymbols = hmm.getSymbols();
357     List<String> strSymbols;
358     strSymbols = charListToStringList(charSymbols);
359     symbolLine += addData(11, 9, strSymbols);
360     file.append(symbolLine + NEW_LINE);
361
362     String transitionTypeLine = "";
363     List<String> transitionTypes;
364     transitionTypes = stringArrayToStringList(hmm.getTransitionTypes());
365     transitionTypeLine += addData(16, 9, transitionTypes);
366     file.append(transitionTypeLine + NEW_LINE);
367
368     int length = hmm.getLength();
369
370     for (int node = 0; node <= length; node++)
371     {
372       String matchLine;
373       if (node == 0)
374       {
375         matchLine = String.format("%7s", "COMPO");
376       }
377       else
378       {
379         matchLine = String.format("%7s", node);
380       }
381
382       List<String> strMatches;
383       List<Double> doubleMatches;
384       doubleMatches = hmm.getNode(node).getMatchEmissions();
385       strMatches = doubleListToStringList(doubleMatches, 5);
386       matchLine += addData(10, 9, strMatches);
387
388
389       if (node != 0)
390       {
391         matchLine += SPACE + hmm.getNodeAlignmentColumn(node);
392         matchLine += SPACE + hmm.getConsensusResidue(node);
393         matchLine += SPACE + hmm.getReferenceAnnotation(node);
394         matchLine += SPACE + hmm.getMaskedValue(node);
395         matchLine += SPACE + hmm.getConsensusStructure(node);
396
397       }
398
399       file.append(matchLine + NEW_LINE);
400       
401       String insertLine = EMPTY;
402       List<String> strInserts;
403       List<Double> doubleInserts;
404       doubleInserts = hmm.getNode(node).getInsertEmissions();
405       strInserts = doubleListToStringList(doubleInserts, 5);
406       insertLine += addData(17, 9, strInserts);
407
408       file.append(insertLine + NEW_LINE);
409
410       String transitionLine = EMPTY;
411       List<String> strTransitions;
412       List<Double> doubleTransitions;
413       doubleTransitions = hmm.getNode(node).getStateTransitions();
414       strTransitions = doubleListToStringList(doubleTransitions, 5);
415       transitionLine += addData(17, 9, strTransitions);
416
417       file.append(transitionLine + NEW_LINE);
418     }
419   }
420
421   void appendFileProperties(StringBuilder file)
422   {
423     String line;
424
425     file.append(fileHeader + NEW_LINE);
426     
427     line = String.format("%-5s %1s", "NAME", hmm.getName());
428     file.append((line + NEW_LINE));
429
430     if (hmm.getAccessionNumber() != null)
431     {
432     line = String.format("%-5s %1s", "ACC", hmm.getAccessionNumber());
433     file.append((line + NEW_LINE));
434     }
435
436     if (hmm.getDescription() != null)
437     {
438     line = String.format("%-5s %1s", "DESC", hmm.getDescription());
439     file.append((line + NEW_LINE));
440     }
441     line = String.format("%-5s %1s", "LENG", hmm.getLength());
442     file.append((line + NEW_LINE));
443
444     if (hmm.getMaxInstanceLength() != null)
445     {
446     line = String.format("%-5s %1s", "MAXL", hmm.getMaxInstanceLength());
447     file.append((line + NEW_LINE));
448     }
449     line = String.format("%-5s %1s", "ALPH", hmm.getAlphabetType());
450     file.append((line + NEW_LINE));
451
452     boolean status;
453     String statusStr;
454
455     status = hmm.referenceAnnotationIsActive();
456     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
457     line = String.format("%-5s %1s", "RF",
458             statusStr);
459     file.append((line + NEW_LINE));
460
461     status = hmm.maskValueIsActive();
462     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
463     line = String.format("%-5s %1s", "MM",
464             statusStr);
465     file.append((line + NEW_LINE));
466     
467     status = hmm.consensusResidueIsActive();
468     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
469     line = String.format("%-5s %1s", "CONS",
470             statusStr);
471     file.append((line + NEW_LINE));
472
473     status = hmm.consensusStructureIsActive();
474     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
475     line = String.format("%-5s %1s", "CS",
476             statusStr);
477     file.append((line + NEW_LINE));
478
479     status = hmm.mapIsActive();
480     statusStr = HiddenMarkovModel.findStringFromBoolean(status);
481     line = String.format("%-5s %1s", "MAP",
482             statusStr);
483     file.append((line + NEW_LINE));
484
485
486     if (hmm.getDate() != null)
487     {
488     line = String.format("%-5s %1s", "DATE", hmm.getDate());
489     file.append((line + NEW_LINE));
490     }
491     if (hmm.getNumberOfSequences() != null)
492     {
493     line = String.format("%-5s %1s", "NSEQ", hmm.getNumberOfSequences());
494     file.append((line + NEW_LINE));
495     }
496     if (hmm.getEffectiveNumberOfSequences() != null)
497     {
498     line = String.format("%-5s %1s", "EFFN",
499             hmm.getEffectiveNumberOfSequences());
500     file.append((line + NEW_LINE));
501     }
502     if (hmm.getCheckSum() != null)
503     {
504     line = String.format("%-5s %1s", "CKSUM", hmm.getCheckSum());
505     file.append((line + NEW_LINE));
506     }
507     if (hmm.getGatheringThreshold() != null)
508     {
509     line = String.format("%-5s %1s", "GA", hmm.getGatheringThreshold());
510     file.append((line + NEW_LINE));
511     }
512
513     if (hmm.getTrustedCutoff() != null)
514     {
515     line = String.format("%-5s %1s", "TC", hmm.getTrustedCutoff());
516     file.append((line + NEW_LINE));
517     }
518     if (hmm.getNoiseCutoff() != null)
519     {
520     line = String.format("%-5s %1s", "NC", hmm.getNoiseCutoff());
521     file.append((line + NEW_LINE));
522     }
523     if (hmm.getMSV() != null)
524     {
525       line = String.format("%-19s %18s", "STATS LOCAL MSV", hmm.getMSV());
526       file.append((line + NEW_LINE));
527
528       line = String.format("%-19s %18s", "STATS LOCAL VITERBI",
529               hmm.getViterbi());
530       file.append((line + NEW_LINE));
531     
532       line = String.format("%-19s %18s", "STATS LOCAL FORWARD",
533               hmm.getForward());
534       file.append((line + NEW_LINE));
535     }
536   }
537
538
539
540   char charValue(String string)
541   {
542     char character;
543     character = string.charAt(0);
544     return character;
545   }
546
547   @Override
548   public String print(SequenceI[] seqs, boolean jvsuffix)
549   {
550
551     return null;
552   }
553 }
554