JAL-2629 storage and lifecycle of alignment/group HMM annotations revised
[jalview.git] / src / jalview / hmmer / HMMSearch.java
1 package jalview.hmmer;
2
3 import jalview.datamodel.Alignment;
4 import jalview.datamodel.AlignmentAnnotation;
5 import jalview.datamodel.AlignmentI;
6 import jalview.datamodel.Annotation;
7 import jalview.datamodel.HiddenMarkovModel;
8 import jalview.datamodel.SequenceI;
9 import jalview.gui.AlignFrame;
10 import jalview.gui.JvOptionPane;
11 import jalview.io.DataSourceType;
12 import jalview.io.FileParse;
13 import jalview.io.StockholmFile;
14 import jalview.util.MessageManager;
15 import jalview.ws.params.ArgumentI;
16 import jalview.ws.params.simple.BooleanOption;
17
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.FileReader;
21 import java.io.IOException;
22 import java.io.PrintWriter;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Scanner;
26
27 import javax.swing.JOptionPane;
28
29 public class HMMSearch extends HmmerCommand
30 {
31   static final String HMMSEARCH = "hmmsearch";
32
33   boolean realign = false;
34
35   boolean trim = false;
36
37   int seqsToReturn = Integer.MAX_VALUE;
38
39   SequenceI[] seqs;
40
41   /**
42    * Constructor for the HMMSearchThread
43    * 
44    * @param af
45    */
46   public HMMSearch(AlignFrame af, List<ArgumentI> args)
47   {
48     super(af, args);
49   }
50
51   /**
52    * Runs the HMMSearchThread: the data on the alignment or group is exported,
53    * then the command is executed in the command line and then the data is
54    * imported and displayed in a new frame. Call this method directly to execute
55    * synchronously, or via start() in a new Thread for asynchronously.
56    */
57   @Override
58   public void run()
59   {
60     HiddenMarkovModel hmm = af.getSelectedHMM();
61     if (hmm == null)
62     {
63       JOptionPane.showMessageDialog(af,
64               MessageManager.getString("warn.no_selected_hmm"));
65       return;
66     }
67
68     SequenceI hmmSeq = af.getSelectedHMMSequence();
69     long msgId = System.currentTimeMillis();
70     af.setProgressBar(MessageManager.getString("status.running_hmmsearch"),
71             msgId);
72
73     try
74     {
75       File hmmFile = createTempFile("hmm", ".hmm");
76       File hitsAlignmentFile = createTempFile("hitAlignment", ".sto");
77       File searchOutputFile = createTempFile("searchOutput", ".sto");
78
79       exportHmm(hmm, hmmFile.getAbsoluteFile());
80
81       boolean ran = runCommand(searchOutputFile, hitsAlignmentFile, hmmFile);
82       if (!ran)
83       {
84         JvOptionPane.showInternalMessageDialog(af,
85                 MessageManager.getString("warn.hmmsearch_failed"));
86         return;
87       }
88
89       importData(hmmSeq, hitsAlignmentFile, hmmFile, searchOutputFile);
90       // TODO make realignment of search results a step at this level
91       // and make it conditional on this.realign
92     } catch (IOException | InterruptedException e)
93     {
94       e.printStackTrace();
95     }
96     finally
97     {
98       af.setProgressBar("", msgId);
99     }
100   }
101
102   /**
103    * Executes an hmmsearch with the given hmm as input. The database to be
104    * searched is a local file as specified by the 'Database' parameter, or the
105    * current alignment (written to file) if none is specified.
106    * 
107    * @param searchOutputFile
108    * @param hitsAlignmentFile
109    * @param hmmFile
110    * 
111    * @return
112    * @throws IOException
113    */
114   private boolean runCommand(File searchOutputFile, File hitsAlignmentFile,
115           File hmmFile) throws IOException
116   {
117     String command = getCommandPath(HMMSEARCH);
118     if (command == null)
119     {
120       return false;
121     }
122
123     List<String> args = new ArrayList<>();
124     args.add(command);
125     args.add("-o");
126     args.add(searchOutputFile.getAbsolutePath());
127     args.add("-A");
128     args.add(hitsAlignmentFile.getAbsolutePath());
129
130     boolean dbFound = false;
131     String dbPath = "";
132     File databaseFile = null;
133
134     if (params != null)
135     {
136       for (ArgumentI arg : params)
137       {
138         String name = arg.getName();
139         if (MessageManager.getString("label.number_of_results")
140                 .equals(name))
141         {
142           seqsToReturn = Integer.parseInt(arg.getValue());
143         }
144         else if (MessageManager.getString("label.auto_align_seqs")
145                 .equals(name))
146         {
147           realign = true; // TODO: not used
148         }
149         else if (MessageManager.getString("label.use_accessions")
150                 .equals(name))
151         {
152           args.add("--acc");
153         }
154         else if (MessageManager.getString("label.seq_e_value").equals(name))
155         {
156           args.add("--incE");
157           args.add(arg.getValue());
158         }
159         else if (MessageManager.getString("label.seq_score").equals(name))
160         {
161           args.add("-incT");
162           args.add(arg.getValue());
163         }
164         else if (MessageManager.getString("label.dom_e_value_desc")
165                 .equals(name))
166         {
167           args.add("--incdomE");
168           args.add(arg.getValue());
169         }
170         else if (MessageManager.getString("label.dom_score").equals(name))
171         {
172           args.add("--incdomT");
173           args.add(arg.getValue());
174         }
175         else if (MessageManager.getString("label.trim_termini")
176                 .equals(name))
177         {
178           trim = true;
179         }
180         else if (MessageManager.getString("label.database").equals(name))
181         {
182           dbFound = true;
183           dbPath = arg.getValue();
184           if (!MessageManager.getString("label.this_alignment")
185                   .equals(dbPath))
186           {
187             databaseFile = new File(dbPath);
188           }
189         }
190       }
191     }
192
193     if (!dbFound || MessageManager.getString("label.this_alignment")
194             .equals(dbPath))
195     {
196       AlignmentI alignment = af.getViewport().getAlignment();
197       AlignmentI copy = new Alignment(alignment);
198       SequenceI hmms = copy.getHmmConsensus();
199       if (hmms != null)
200       {
201         copy.deleteSequence(hmms);
202       }
203       StockholmFile stoFile = new StockholmFile(copy);
204       stoFile.setSeqs(copy.getSequencesArray());
205       String alignmentString = stoFile.print();
206       databaseFile = createTempFile("database", ".sto");
207       PrintWriter writer = new PrintWriter(databaseFile);
208       writer.print(alignmentString);
209       writer.close();
210     }
211
212     args.add(hmmFile.getAbsolutePath());
213     args.add(databaseFile.getAbsolutePath());
214
215     return runCommand(args);
216   }
217
218   /**
219    * Imports the data from the temporary file to which the output of hmmsearch
220    * is directed.
221    * 
222    * @param hmmSeq
223    */
224   private void importData(SequenceI hmmSeq, File inputAlignmentTemp,
225           File hmmTemp, File searchOutputFile)
226           throws IOException, InterruptedException
227   {
228     BufferedReader br = new BufferedReader(
229             new FileReader(inputAlignmentTemp));
230     try
231     {
232       if (br.readLine() == null)
233       {
234         JOptionPane.showMessageDialog(af,
235                 MessageManager.getString("label.no_sequences_found"));
236         return;
237       }
238       StockholmFile file = new StockholmFile(new FileParse(
239               inputAlignmentTemp.getAbsolutePath(), DataSourceType.FILE));
240       seqs = file.getSeqsAsArray();
241
242       readTable(searchOutputFile);
243
244       int seqCount = Math.min(seqs.length, seqsToReturn);
245       SequenceI[] hmmAndSeqs = new SequenceI[seqCount + 1];
246       hmmAndSeqs[0] = hmmSeq;
247       System.arraycopy(seqs, 0, hmmAndSeqs, 1, seqCount);
248
249       AlignmentI alignment = new Alignment(hmmAndSeqs);
250       AlignFrame frame = new AlignFrame(alignment, 1, 1);
251       frame.setSelectedHMMSequence(hmmSeq);
252       List<ArgumentI> alignArgs = new ArrayList<>();
253       if (trim)
254       {
255         alignArgs.add(new BooleanOption(
256                 MessageManager.getString("label.trim_termini"),
257                 MessageManager.getString("label.trim_termini_desc"), true,
258                 true, true, null));
259       }
260       HMMAlign hmmalign = new HMMAlign(frame, alignArgs);
261       hmmalign.run();
262       frame = null;
263       hmmTemp.delete();
264       inputAlignmentTemp.delete();
265       searchOutputFile.delete();
266     } finally
267     {
268       if (br != null)
269       {
270         br.close();
271       }
272     }
273   }
274
275   void readTable(File inputTableTemp) throws IOException
276   {
277     BufferedReader br = new BufferedReader(new FileReader(inputTableTemp));
278     String line = "";
279     while (!line.startsWith("Query:"))
280     {
281       line = br.readLine();
282     }
283     for (int i = 0; i < 5; i++)
284     {
285       line = br.readLine();
286     }
287
288     int index = 0;
289     while (!"  ------ inclusion threshold ------".equals(line)
290             && !"".equals(line))
291     {
292       Scanner scanner = new Scanner(line);
293
294       String str = scanner.next(); // full sequence eValue score
295       float eValue = Float.parseFloat(str);
296       int seqLength = seqs[index].getLength();
297       Annotation[] annots = new Annotation[seqLength];
298       for (int j = 0; j < seqLength; j++)
299       {
300         annots[j] = new Annotation(eValue);
301       }
302       AlignmentAnnotation annot = new AlignmentAnnotation("E-value",
303               "Score", annots);
304       annot.setScore(Double.parseDouble(str));
305       annot.setSequenceRef(seqs[index]);
306       seqs[index].addAlignmentAnnotation(annot);
307
308       scanner.close();
309       line = br.readLine();
310       index++;
311     }
312
313     br.close();
314   }
315
316 }