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