168e3f9de3d2157ed7eb8bf1a28e396536853126
[jalview.git] / src / jalview / hmmer / HMMAlignThread.java
1 package jalview.hmmer;
2
3 import jalview.bin.Cache;
4 import jalview.datamodel.Alignment;
5 import jalview.datamodel.AlignmentI;
6 import jalview.datamodel.AlignmentOrder;
7 import jalview.datamodel.AlignmentView;
8 import jalview.datamodel.HiddenColumns;
9 import jalview.datamodel.HiddenMarkovModel;
10 import jalview.datamodel.SequenceI;
11 import jalview.gui.AlignFrame;
12 import jalview.gui.Desktop;
13 import jalview.gui.JvOptionPane;
14 import jalview.gui.Preferences;
15 import jalview.gui.SplitFrame;
16 import jalview.io.DataSourceType;
17 import jalview.io.StockholmFile;
18 import jalview.util.MessageManager;
19 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
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 import java.util.Map;
27
28 import javax.swing.JInternalFrame;
29
30 public class HMMAlignThread implements Runnable
31 {
32
33   /**
34    * feature settings from view that job was associated with
35    */
36   protected FeatureRendererSettings featureSettings = null;
37
38   /**
39    * Object containing frequently used commands.
40    */
41   HMMERCommands cmds = new HMMERCommands();
42
43   AlignFrame af;
44
45   AlignmentI alignment;
46
47   AlignmentI dataset;
48
49   List<AlignmentOrder> orders;
50
51   AlignmentView msa;
52
53   HiddenMarkovModel hmm;
54
55   List<ArgumentI> args;
56
57   boolean newFrame;
58
59   long barID;
60
61   Map<Integer, SequenceI> hmmSeqs;
62
63   File hmmTemp = null;
64
65   File outTemp = null;
66
67   File inputTemp = null;
68
69   List<AlignmentOrder> allOrders;
70
71   SequenceI[][] allResults;
72
73   /**
74    * Constructor for the HMMAlignThread. If create new frame is set to true, a
75    * new frame will be created.
76    * 
77    * @param af
78    * @param createNewFrame
79    */
80   public HMMAlignThread(AlignFrame af, boolean createNewFrame,
81           List<ArgumentI> args)
82   {
83     this.af = af;
84     alignment = af.getViewport().getAlignment();
85     if (alignment.getDataset() != null)
86     {
87       dataset = alignment.getDataset();
88     }
89     newFrame = createNewFrame;
90     featureSettings = af.getFeatureRenderer().getSettings();
91     this.args = args;
92   }
93
94   /**
95    * Runs the HMMAlignThread: the data on the alignment or group is exported,
96    * then the command is executed in the command line and then the data is
97    * imported and displayed in a new frame (if true). The command is executed
98    * for each segemtn of the alignment.
99    */
100   @Override
101   public void run()
102   {
103
104     hmm = af.getSelectedHMM();
105
106     barID = System.currentTimeMillis();
107     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
108             barID);
109     cmds.HMMERFOLDER = Cache.getProperty(Preferences.HMMER_PATH);
110     prepareAlignment();
111     SequenceI[][] subAlignments = msa.getVisibleContigs('-');
112     allOrders = new ArrayList<>();
113     allResults = new SequenceI[subAlignments.length][];
114     int job = 0;
115     for (SequenceI[] seqs : subAlignments)
116     {
117       cmds.uniquifySequences(seqs);
118       try
119       {
120         createTemporaryFiles();
121       } catch (IOException e2)
122       {
123         e2.printStackTrace();
124       }
125       try
126       {
127         cmds.exportData(seqs, outTemp.getAbsoluteFile(), hmm,
128                 hmmTemp.getAbsoluteFile(), null);
129       } catch (IOException e1)
130       {
131         e1.printStackTrace();
132       }
133       try
134       {
135         boolean ran = runCommand();
136         if (!ran)
137         {
138           JvOptionPane.showInternalMessageDialog(af,
139                   MessageManager.getString("warn.hmmalign_failed"));
140           return;
141         }
142       } catch (IOException | InterruptedException e)
143       {
144         e.printStackTrace();
145       }
146       try
147       {
148         importData(job);
149       } catch (IOException | InterruptedException e)
150       {
151         // TODO Auto-generated catch block
152         e.printStackTrace();
153       }
154       job++;
155     }
156
157     displayResults(newFrame);
158
159     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
160             barID);
161
162   }
163
164   /**
165    * Creates temporary files for exporting and importing the data.
166    * 
167    * @throws IOException
168    */
169   private void createTemporaryFiles() throws IOException
170   {
171     if (hmmTemp == null)
172     {
173       hmmTemp = File.createTempFile("hmm", ".hmm");
174       hmmTemp.deleteOnExit();
175     }
176     if (outTemp == null)
177     {
178       outTemp = File.createTempFile("output", ".sto");
179       outTemp.deleteOnExit();
180     }
181     inputTemp = File.createTempFile("input", ".sto");
182     inputTemp.deleteOnExit();
183   }
184
185   /**
186    * Executes the hmmalign command in the command line.
187    * 
188    * @return
189    * @throws IOException
190    * @throws InterruptedException
191    */
192   private boolean runCommand() throws IOException, InterruptedException
193   {
194     File file = new File(cmds.HMMERFOLDER + "/hmmalign");
195     if (!file.canExecute())
196     {
197       file = new File(cmds.HMMERFOLDER + "/hmmalign.exe");
198       {
199         if (!file.canExecute())
200         {
201           return false;
202         }
203       }
204     }
205     String command = cmds.HMMERFOLDER + cmds.HMMALIGN;
206     String version = Cache.getProperty("HMMER_VERSION");
207     if (!"3.1b2".equals(version))
208     {
209       command += cmds.ALLCOL;
210     }
211     if (args != null)
212     {
213       for (ArgumentI arg : args)
214       {
215         String name = arg.getName();
216         switch (name)
217         {
218         case "Trim Non-Matching Termini":
219           command += "--trim";
220         }
221       }
222     }
223     command += " -o " + inputTemp.getAbsolutePath() + cmds.SPACE
224             + hmmTemp.getAbsolutePath() + cmds.SPACE
225             + outTemp.getAbsolutePath();
226     return cmds.runCommand(command);
227   }
228
229   /**
230    * Imports the data from the temporary file to which the output of hmmalign is
231    * directed. this is used for an internal job.
232    * 
233    * @param index
234    *          The index of the 'job' (or region of an alignment).
235    * @throws IOException
236    * @throws InterruptedException
237    */
238   private void importData(int index)
239           throws IOException, InterruptedException
240   {
241     StockholmFile file = new StockholmFile(inputTemp.getAbsolutePath(),
242             DataSourceType.FILE);
243     SequenceI[] result = file.getSeqsAsArray();
244     AlignmentOrder msaorder = new AlignmentOrder(result);
245     jalview.analysis.AlignmentSorter.recoverOrder(result);
246     jalview.analysis.SeqsetUtils.deuniquify(cmds.hash, result);
247     allOrders.add(msaorder);
248     allResults[index] = result;
249     hmmTemp.delete();
250     outTemp.delete();
251     inputTemp.delete();
252   }
253
254   /**
255    * Gathers the sequences in preparation for the alignment.
256    */
257   private void prepareAlignment()
258   {
259     // hmmSeqs = alignment.getHMMConsensusSequences(true);
260     msa = af.gatherSequencesForAlignment();
261   }
262
263   /**
264    * Displays the results of all 'jobs'.
265    * 
266    * @param newFrame
267    */
268   private void displayResults(boolean newFrame)
269   {
270     AlignmentOrder[] arrOrders = allOrders
271             .toArray(new AlignmentOrder[allOrders.size()]);
272     Object[] newview = msa.getUpdatedView(allResults, arrOrders, '-');
273     SequenceI[] alignment = (SequenceI[]) newview[0];
274     HiddenColumns hidden = (HiddenColumns) newview[1];
275     Alignment al = new Alignment(alignment);
276     al.setProperty("Alignment Program", "hmmalign");
277     if (dataset != null)
278     {
279       al.setDataset(dataset);
280     }
281
282     if (newFrame)
283     {
284       displayInNewFrame(al, allOrders, hidden);
285     }
286   }
287
288   /**
289    * Displays the results in a new frame.
290    * 
291    * @param al
292    *          The alignment containing the results.
293    * @param alorders
294    *          The order of the sequences in the alignment on which the jobs were
295    *          run.
296    * @param hidden
297    *          Hidden columns in the previous alignment.
298    */
299   private void displayInNewFrame(AlignmentI al,
300           List<AlignmentOrder> alorders, HiddenColumns hidden)
301   {
302     AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
303             AlignFrame.DEFAULT_HEIGHT);
304
305     // initialise with same renderer settings as in parent alignframe.
306     af.getFeatureRenderer().transferSettings(this.featureSettings);
307
308     if (allOrders.size() > 0)
309     {
310       addSortByMenuItems(af, allOrders);
311     }
312
313     // TODO: refactor retrieve and show as new splitFrame as Desktop method
314
315     /*
316      * If alignment was requested from one half of a SplitFrame, show in a
317      * SplitFrame with the other pane similarly aligned.
318      */
319     AlignFrame requestedBy = this.af;
320     if (requestedBy != null && requestedBy.getSplitViewContainer() != null
321             && requestedBy.getSplitViewContainer()
322                     .getComplement(requestedBy) != null)
323     {
324       AlignmentI complement = requestedBy.getSplitViewContainer()
325               .getComplement(requestedBy);
326       String complementTitle = requestedBy.getSplitViewContainer()
327               .getComplementTitle(requestedBy);
328       // becomes null if the alignment window was closed before the alignment
329       // job finished.
330       AlignmentI copyComplement = new Alignment(complement);
331       // todo should this be done by copy constructor?
332       copyComplement.setGapCharacter(complement.getGapCharacter());
333       // share the same dataset (and the mappings it holds)
334       copyComplement.setDataset(complement.getDataset());
335       copyComplement.alignAs(al);
336       if (copyComplement.getHeight() > 0)
337       {
338         af.setTitle(this.af.getTitle());
339         AlignFrame af2 = new AlignFrame(copyComplement,
340                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
341         af2.setTitle(complementTitle);
342         String linkedTitle = MessageManager
343                 .getString("label.linked_view_title");
344         JInternalFrame splitFrame = new SplitFrame(
345                 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
346         Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
347         return;
348       }
349     }
350
351     /*
352      * Not from SplitFrame, or failed to created a complementary alignment
353      */
354     Desktop.addInternalFrame(af, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
355             AlignFrame.DEFAULT_HEIGHT);
356   }
357
358   /**
359    * Add sort order options to the AlignFrame menus.
360    * 
361    * @param af
362    * @param alorders
363    */
364   protected void addSortByMenuItems(AlignFrame af,
365           List<AlignmentOrder> alorders)
366   {
367     // update orders
368     if (alorders.size() == 1)
369     {
370       af.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
371     }
372     else
373     {
374       // construct a non-redundant ordering set
375       List<String> names = new ArrayList<>();
376       for (int i = 0, l = alorders.size(); i < l; i++)
377       {
378         String orderName = " Region " + i;
379         int j = i + 1;
380
381         while (j < l)
382         {
383           if (alorders.get(i).equals(alorders.get(j)))
384           {
385             alorders.remove(j);
386             l--;
387             orderName += "," + j;
388           }
389           else
390           {
391             j++;
392           }
393         }
394
395         if (i == 0 && j == 1)
396         {
397           names.add("");
398         }
399         else
400         {
401           names.add(orderName);
402         }
403       }
404       for (int i = 0, l = alorders.size(); i < l; i++)
405       {
406         af.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering",
407                 alorders.get(i));
408       }
409     }
410   }
411
412   /**
413    * Runs hmmalign, and waits for the results to be imported before continuing
414    */
415   public void hmmalignWaitTillComplete()
416   {
417     Thread loader = new Thread(this);
418     loader.start();
419
420     while (loader.isAlive())
421     {
422       try
423       {
424         Thread.sleep(500);
425       } catch (Exception ex)
426       {
427       }
428     }
429
430   }
431 }