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