add HMMFile class to read and write HMM files
[jalview.git] / src / jalview / io / HMMFile.java
1 package jalview.io;
2
3 import jalview.datamodel.EValueStatistic;
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.PrintWriter;
12 import java.io.UnsupportedEncodingException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Scanner;
18
19 /**
20  * reads in and writes out a HMMER standard file
21  * 
22  * 
23  * @author TZVanaalten
24  *
25  */
26 public class HMMFile extends FileParse
27 {
28   // HMM to store file data
29   HiddenMarkovModel hmm = new HiddenMarkovModel();
30
31   // Source of file
32   String dataObject;
33
34   // number of symbols
35   int numberOfSymbols;
36
37   // number of possible transitions
38   final int NUMBER_OF_TRANSITIONS = 7;
39
40   // file header
41   String fileHeader;
42
43   /**
44    * Constructor which contains model to be filled or exported
45    * 
46    * @param dataSource
47    *          Filename, URL or Pasted String to read from
48    */
49   public HMMFile(String dataSource)
50   {
51     dataObject = dataSource;
52   }
53
54   /**
55    * reads data from HMM file
56    * 
57    * @throws IOException
58    */
59   public void parse() throws IOException
60   {
61     File file = new File(dataObject);
62     FileReader fr = new FileReader(file);
63     BufferedReader br = new BufferedReader(fr);
64     parseFileProperties(br);
65     parseModel(br);
66
67   }
68
69   /**
70    * imports file properties from hmm file
71    * 
72    * @param input
73    *          buffered reader used to read in file
74    * @throws IOException
75    */
76   public void parseFileProperties(BufferedReader input) throws IOException
77   {
78     boolean readingFile = true;
79     fileHeader = input.readLine();
80     String line = input.readLine();
81     while (readingFile)
82     {
83       if (line != null)
84       {
85         Scanner parser = new Scanner(line);
86         String next = parser.next();
87         if ("HMM".equals(next)) // indicates start of HMM data (end of file
88                               // properties)
89         {
90           readingFile = false;
91           hmm.fillSymbols(line);
92           numberOfSymbols = hmm.getSymbols().size();
93         }
94         else if ("STATS".equals(next)) // reads e-value stats into separate
95                                        // field
96                                      // on HMM object
97         {
98           readStats(parser);
99         }
100         else if ("GA".equals(next) || "TC".equals(next)
101                 || "NC".equals(next)) // reads
102                                                                             // pfam
103                                                                             // data
104                                                                             // into
105                                                                             // separate
106                                                                             // field
107                                                                             // on
108                                                                             // HMM
109                                                                             // object
110         {
111           Double[] data = new Double[2];
112           data[0] = parser.nextDouble();
113           data[1] = parser.nextDouble();
114           hmm.setPFAMData(next, data);
115         }
116         else
117         {
118           String key = next;
119           String value = parser.next();
120           while (parser.hasNext())
121           {
122             value = value + " " + parser.next();
123           }
124           hmm.put(key, value);
125         }
126         parser.close();
127       }
128       line = input.readLine();
129       if (line == null)
130       {
131         readingFile = false;
132       }
133     }
134
135   }
136
137   /**
138    * creates a new EValueStatistic object to store stats
139    * 
140    * @param parser
141    *          Scanner which contains data for STATS line
142    * 
143    */
144   public void readStats(Scanner parser)
145   {
146     if (parser.hasNext())
147     {
148     String name;
149     double slope;
150     double location;
151     String configuration;
152
153     configuration = parser.next();
154     name = parser.next();
155     slope = parser.nextDouble();
156     location = parser.nextDouble();
157     hmm.addStatistic(name,
158             new EValueStatistic(configuration, slope, location));
159     }
160   }
161
162   /**
163    * parses the model data from the hmm file
164    * 
165    * @param input
166    *          buffered reader used to read file
167    * @throws IOException
168    */
169   public void parseModel(BufferedReader input) throws IOException
170   {
171
172     String line = input.readLine();
173     Scanner scanner = new Scanner(line);
174     String next = scanner.next();
175     if ("COMPO".equals(next)) // checks to and stores COMPO data if present
176     {
177       for (int i = 0; i < numberOfSymbols; i++)
178
179       {
180         hmm.getAverageMatchStateEmissionProbabilities()
181                 .add(scanner.nextDouble());
182       }
183     }
184     scanner.close();
185     parseBeginNodeData(input);
186     for (int i = 0; i < hmm.getLength(); i++)
187     {
188       Scanner matchReader = new Scanner(input.readLine());
189       matchReader.nextInt(); // skips number indicating position in HMM
190       hmm.getMatchEmissions()
191               .add(fillList(matchReader, numberOfSymbols));
192       parseAnnotations(matchReader, i);
193       matchReader.close();
194       Scanner insertReader = new Scanner(input.readLine());
195       hmm.getInsertEmissions().add(fillList(insertReader, numberOfSymbols));
196       insertReader.close();
197       Scanner transitionReader = new Scanner(input.readLine());
198       hmm.getStateTransitions()
199               .add(fillList(transitionReader, NUMBER_OF_TRANSITIONS));
200       transitionReader.close();
201     }
202
203   }
204
205   /**
206    * parses the begin state transitions and insert 0 emissions
207    * 
208    * @param input
209    *          buffered reader used to read model
210    * @param currentline
211    *          string contain all data on current line of buffered reader
212    * @throws IOException
213    */
214
215   public void parseBeginNodeData(BufferedReader input)
216           throws IOException
217   {
218     Scanner scanner = new Scanner(input.readLine());
219     hmm.setInsertZeroEmissions(fillList(scanner, hmm.getSymbols().size()));
220     scanner.close();
221     Scanner scannerTransitions = new Scanner(input.readLine());
222     hmm.setBeginStateTransitions(
223             fillList(scannerTransitions, NUMBER_OF_TRANSITIONS));
224     scannerTransitions.close();
225   }
226
227   /**
228    * parses annotations on match emission line
229    * 
230    * @param scanner
231    *          scanner which is processing match emission line
232    * @param index
233    *          index of node which is beign scanned
234    */
235   public void parseAnnotations(Scanner scanner, int index)
236   {
237     if (hmm.getMapAnnotationFlag())
238     {
239       hmm.getAlignmentColumnIndexes().add(scanner.nextInt());
240     }
241     else
242     {
243       scanner.next();
244     }
245     hmm.getAnnotations().add(new HashMap<String, Character>());
246     hmm.getAnnotations().get(index).put("CONS", scanner.next().charAt(0));
247     hmm.getAnnotations().get(index).put("RF", scanner.next().charAt(0));
248     hmm.getAnnotations().get(index).put("MM", scanner.next().charAt(0));
249     hmm.getAnnotations().get(index).put("CS", scanner.next().charAt(0));
250   }
251   /**
252    * 
253    * @param transition
254    *          type of transition occuring
255    * @return index value representing position along stateTransition array.
256    */
257   public Integer getTransitionType(String transition)
258   {
259     Integer index;
260     switch (transition)
261     {
262     case "mm":
263       index = 0;
264       break;
265     case "mi":
266       index = 1;
267       break;
268     case "md":
269       index = 2;
270       break;
271     case "im":
272       index = 3;
273       break;
274     case "ii":
275       index = 4;
276       break;
277     case "dm":
278       index = 5;
279       break;
280     case "dd":
281       index = 6;
282       break;
283     default:
284       index = null;
285     }
286     return index;
287   }
288
289   /**
290    * 
291    * @param input
292    *          scanner for line containing data to be transferred to list
293    * @param numberOfElements
294    *          number of elements in the list to be filled
295    * @return filled list
296    */
297   public static List<Double> fillList(Scanner input,
298           int numberOfElements)
299   {
300     List<Double> list = new ArrayList<>();
301     String next;
302     for (int i = 0; i < numberOfElements; i++)
303     {
304       next = input.next();
305       if (next.contains("*")) // state transitions to or from delete states
306                               // occasionally have values of -infinity. These
307                               // values are represented by an * in the .hmm
308                               // file, and by a null value in the
309                               // HiddenMarkovModel class
310       {
311         list.add(null);
312       }
313       else
314       {
315         list.add(Double.valueOf(next));
316       }
317     }
318     return list;
319   }
320
321   /**
322    * writes a HiddenMarkovModel to a file. Needs mode work to make file more
323    * readable for humans (align columns)
324    * 
325    * @param exportLocation
326    *          Filename, URL or Pasted String to write to
327    * @throws FileNotFoundException
328    * @throws UnsupportedEncodingException
329    */
330   public void exportFile(String exportLocation)
331           throws FileNotFoundException, UnsupportedEncodingException
332   {
333     PrintWriter writer = new PrintWriter(exportLocation, "UTF-8");
334     writer.println(fileHeader);
335     for (Map.Entry<String, String> entry : hmm.getFileProperties()
336             .entrySet())
337     {
338       writer.println(entry.getKey() + " " + entry.getValue());
339     }
340     writer.println(
341             "HMM" + " " + convertCharListToString(hmm.getSymbols()));
342     writer.println("m->m m->i m->d i->m i->i d->m d->d");
343     if (false == hmm.getAverageMatchStateEmissionProbabilities().isEmpty())
344     {
345       writer.println("COMPO" + " " + convertDoubleListToString(
346               hmm.getAverageMatchStateEmissionProbabilities()));
347     }
348     writer.println(convertDoubleListToString(hmm.getInsertZeroEmissions()));
349     writer.println(
350             convertDoubleListToString(hmm.getBeginStateTransitions()));
351
352     for (Integer i = 0; i < hmm.getLength(); i++)
353     {
354       String matchEmissionLine = i.toString() + " "; // adds node index
355       matchEmissionLine += convertDoubleListToString(
356               hmm.getMatchEmissions().get(i)); // adds match emissions
357       matchEmissionLine += " "
358               + hmm.getAlignmentColumnIndexes().get(i).toString(); // adds MAP
359                                                                    // annotation
360       matchEmissionLine += " "
361               + hmm.getAnnotations().get(i).get("CONS").toString(); // adds CONS
362                                                                     // annotation
363       matchEmissionLine += " "
364               + hmm.getAnnotations().get(i).get("RF").toString(); // adds RF
365                                                                   // annotation
366       matchEmissionLine += " "
367               + hmm.getAnnotations().get(i).get("MM").toString(); // adds MM
368                                                                   // annotation
369       matchEmissionLine += " "
370               + hmm.getAnnotations().get(i).get("CS").toString(); // adds CS
371                                                                   // annotation
372       writer.println(matchEmissionLine);
373
374       writer.println(
375               convertDoubleListToString(hmm.getInsertEmissions().get(i)));
376       writer.println(
377               convertDoubleListToString(hmm.getStateTransitions().get(i)));
378     }
379     writer.println("//");
380
381     writer.close();
382   }
383
384   /**
385    * converts an list of characters to a string with items separated by spaces
386    * 
387    * @param list
388    *          character list to be converted
389    * @return string value of char list
390    */
391   public String convertCharListToString(List<Character> list)
392   {
393     String string = "";
394     for (Character item : list)
395     {
396       string = string + item.toString() + " ";
397     }
398
399     return string;
400   }
401   
402   /**
403    * converts an list of doubles to a string with items separated by spaces
404    * 
405    * @param list
406    *          double list to be converted
407    * @return string value of double list
408    */
409   public String convertDoubleListToString(List<Double> list)
410   {
411     String string = "";
412     for (Double item : list)
413     {
414       if (item != null)
415       {
416         string = string + item.toString() + " ";
417       }
418       else
419       {
420         string = string + "*" + " ";
421       }
422
423     }
424
425     return string;
426   }
427 }
428