JAL-2717 JAL-2668 fixes to HMMER colour scheme display names, enabled state, test...
[jalview.git] / src / jalview / hmmer / HMMBuildThread.java
1 package jalview.hmmer;
2
3 import jalview.api.AlignViewportI;
4 import jalview.datamodel.AlignmentI;
5 import jalview.datamodel.Sequence;
6 import jalview.datamodel.SequenceGroup;
7 import jalview.datamodel.SequenceI;
8 import jalview.gui.AlignFrame;
9 import jalview.gui.AlignViewport;
10 import jalview.gui.JvOptionPane;
11 import jalview.io.DataSourceType;
12 import jalview.io.FileParse;
13 import jalview.io.HMMFile;
14 import jalview.util.MessageManager;
15 import jalview.ws.params.ArgumentI;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import javax.swing.JOptionPane;
23
24 public class HMMBuildThread extends HmmerCommand implements Runnable
25 {
26   static final String ARG_AMINO = "--amino";
27
28   static final String ARG_DNA = "--dna";
29
30   static final String ARG_RNA = "--rna";
31
32   AlignmentI alignment;
33
34   /**
35    * Constructor
36    * 
37    * @param alignFrame
38    * @param args
39    */
40   public HMMBuildThread(AlignFrame alignFrame, List<ArgumentI> args)
41   {
42     super(alignFrame, args);
43   }
44
45   /**
46    * Builds a HMM from an alignment (and/or groups), then imports and adds it to
47    * the alignment (and/or groups)
48    */
49   @Override
50   public void run()
51   {
52     long msgID = System.currentTimeMillis();
53     if (af != null)
54     {
55       af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
56               msgID);
57     }
58
59     AlignViewportI viewport = af.getViewport();
60     try
61     {
62       if (viewport != null)
63       {
64         alignment = viewport.getAlignment();
65       }
66       List<SequenceGroup> groups = new ArrayList<>();
67       if (params != null)
68       {
69         for (ArgumentI arg : params)
70         {
71           String name = arg.getName();
72           if (MessageManager.getString("label.hmmbuild_for").equals(name))
73           {
74             String value = arg.getValue();
75             if (MessageManager.getString("label.alignment").equals(value))
76             {
77               alignment = viewport.getAlignment();
78             }
79             else if (MessageManager.getString("label.groups_and_alignment")
80                     .equals(value))
81             {
82               alignment = viewport.getAlignment();
83               groups.addAll(viewport.getAlignment().getGroups());
84             }
85             else if (MessageManager.getString("label.groups").equals(value))
86             {
87               alignment = null;
88               groups = viewport.getAlignment().getGroups();
89             }
90             else if ("label.selected_group".equals(value))
91             {
92               alignment = null;
93               groups.add(viewport.getSelectionGroup());
94             }
95           }
96           else if (MessageManager.getString("label.use_reference")
97                   .equals(name))
98           {
99             // todo disable this option if no RF annotation on alignment
100             if (!af.getViewport().hasReferenceAnnotation())
101             {
102               JvOptionPane.showInternalMessageDialog(af, MessageManager
103                       .getString("warn.no_reference_annotation"));
104               // return;
105             }
106           }
107         }
108       }
109
110       if (alignment != null)
111       {
112         runHMMBuild(null);
113       }
114
115       if (alignment == null)
116       {
117         alignment = viewport.getAlignment();
118       }
119
120       for (SequenceGroup grp : groups)
121       {
122         runHMMBuild(grp);
123       }
124     } finally
125     {
126       if (af != null)
127       {
128         af.buildColourMenu(); // enable HMMER colour schemes
129         af.setProgressBar("", msgID);
130       }
131     }
132   }
133
134   /**
135    * Runs hmmbuild on the alignment, or on the group if one is specified
136    * 
137    * @param grp
138    */
139   private void runHMMBuild(SequenceGroup group)
140   {
141     if (alignment == null && group == null)
142     {
143       JOptionPane.showMessageDialog(af,
144               MessageManager.getString("warn.no_sequence_data"));
145     }
146     File hmmFile = null;
147     File alignmentFile = null;
148     try
149     {
150       hmmFile = createTempFile("hmm", ".hmm");
151       alignmentFile = createTempFile("output", ".sto");
152       SequenceI[] array;
153       List<SequenceI> hmmSeqs = null;
154       if (group != null)
155       {
156         hmmSeqs = group.getHMMConsensusSequences();
157         array = group.getSelectionAsNewSequences(alignment);
158       }
159       else
160       {
161         hmmSeqs = alignment.getHMMConsensusSequences();
162         // todo pad gaps in an unaligned SequenceGroup as well?
163         if (!alignment.isAligned())
164         {
165           alignment.padGaps();
166         }
167         array = alignment.getSequencesArray();
168       }
169
170       if (array.length < 1)
171       {
172         if (af != null)
173         {
174           JOptionPane.showMessageDialog(af,
175                   MessageManager.getString("warn.no_sequence_data"));
176         }
177         return;
178       }
179
180       /*
181        * copy over sequences excluding hmm consensus sequences
182        */
183       SequenceI[] newArr = new SequenceI[array.length - hmmSeqs.size()];
184       int index = 0;
185       for (SequenceI seq : array)
186       {
187         if (seq.isHMMConsensusSequence())
188         {
189           alignment.deleteSequence(seq);
190         }
191         else
192         {
193           newArr[index] = new Sequence(seq);
194           index++;
195         }
196       }
197
198       stashSequences(newArr);
199
200       exportStockholm(newArr, alignmentFile,
201               group != null ? group : alignment);
202
203       recoverSequences(array);
204
205       boolean ran = runCommand(alignmentFile, hmmFile, group);
206       if (!ran)
207       {
208         return;
209       }
210       importData(hmmFile, group);
211     } catch (Exception e)
212     {
213       e.printStackTrace();
214     } finally
215     {
216       if (hmmFile != null)
217       {
218         hmmFile.delete();
219       }
220       if (alignmentFile != null)
221       {
222         alignmentFile.delete();
223       }
224     }
225   }
226
227   /**
228    * Constructs and executes the hmmbuild command as a separate process
229    * 
230    * @param sequences
231    *          the alignment from which the HMM is built
232    * @param hmm
233    *          the output file to which the HMM is written
234    * @param group
235    *          (optional) group for which the hmm is generated
236    * 
237    * @return
238    * @throws IOException
239    * @throws InterruptedException
240    */
241   private boolean runCommand(File sequences, File hmm, SequenceGroup group)
242           throws IOException, InterruptedException
243   {
244
245     String cmd = getCommandPath(HMMBUILD);
246     if (cmd == null)
247     {
248       return false;
249     }
250     List<String> args = new ArrayList<>();
251     args.add(cmd);
252     String name = null;
253
254     if (params != null)
255     {
256       for (ArgumentI arg : params)
257       {
258         String argName = arg.getName();
259         switch (argName)
260         {
261         case "HMM Name":
262           name = arg.getValue();
263           name = name.trim();
264           break;
265         case "Use Reference Annotation":
266           args.add("--hand");
267           break;
268         }
269       }
270     }
271
272     if (group != null)
273     {
274       name = group.getName() + "_HMM";
275     }
276
277     if (name == null || "".equals(name))
278     {
279       if (af != null)
280       {
281         if (af.getTitle().length() < 15)
282         {
283           name = af.getTitle();
284         }
285       }
286       if (name == null || "".equals(name))
287       {
288         name = "Alignment";
289       }
290
291     }
292
293     args.add("-n");
294     args.add(name.replace(' ', '_'));
295     if (!alignment.isNucleotide())
296     {
297       args.add(ARG_AMINO); // TODO check for rna
298     }
299     else
300     {
301       args.add(ARG_DNA);
302     }
303
304     args.add(hmm.getAbsolutePath());
305     args.add(sequences.getAbsolutePath());
306
307     return runCommand(args);
308   }
309
310   /**
311    * Imports the .hmm file produced by hmmbuild, and inserts the HMM consensus
312    * sequence (with attached HMM profile) as the first sequence in the alignment
313    * or group for which it was generated
314    * 
315    * @param hmmFile
316    * @oparam group (optional) the group for which the hmm was generated
317    * @throws IOException
318    * @throws InterruptedException
319    */
320   private void importData(File hmmFile, SequenceGroup group)
321           throws IOException, InterruptedException
322   {
323     HMMFile file = new HMMFile(
324             new FileParse(hmmFile.getAbsolutePath(), DataSourceType.FILE));
325     SequenceI[] seqs = file.getSeqsAsArray();
326     SequenceI seq = seqs[0];
327     seq.createDatasetSequence();
328     if (group != null)
329     {
330       seq.insertCharAt(0, group.getStartRes(), '-');
331       seq.insertCharAt(group.getEndRes() + 1,
332               alignment.getWidth() - group.getEndRes() - 1, '-');
333       seq.updateHMMMapping();
334       SequenceI topSeq = group.getSequencesInOrder(alignment)[0];
335       int topIndex = alignment.findIndex(topSeq);
336       alignment.insertSequenceAt(topIndex, seq);
337       group.setSeqrep(seq);
338       group.addSequence(seq, false);
339     }
340     else
341     {
342       alignment.insertSequenceAt(0, seq);
343     }
344
345     AlignViewport viewport = af.getViewport();
346     if (viewport != null)
347     {
348       viewport.alignmentChanged(viewport.getAlignPanel());
349       viewport.getAlignPanel().adjustAnnotationHeight();
350       viewport.updateSequenceIdColours();
351
352       if (viewport.getAlignPanel().alignFrame.getSelectedHMM() == null)
353       {
354         viewport.getAlignPanel().alignFrame.setSelectedHMMSequence(seq);
355       }
356     }
357   }
358
359   /**
360    * Runs hmmbuild, and waits for the results to be imported before continuing
361    */
362   public void hmmbuildWaitTillComplete()
363   {
364     Thread loader = new Thread(this);
365     loader.start();
366
367     while (loader.isAlive())
368     {
369       try
370       {
371         Thread.sleep(500);
372       } catch (Exception ex)
373       {
374       }
375     }
376   }
377 }