Merge remote-tracking branch 'origin/releases/Release_2_10_2_Branch' into features...
[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
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.swing.JInternalFrame;
28 import javax.swing.JOptionPane;
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   boolean newFrame;
56
57   long barID;
58
59   Map<Integer, SequenceI> hmmSeqs;
60
61   File hmmTemp = null;
62
63   File outTemp = null;
64
65   File inputTemp = null;
66
67   List<AlignmentOrder> allOrders;
68
69   SequenceI[][] allResults;
70
71   /**
72    * Constructor for the HMMAlignThread. If create new frame is set to true, a
73    * new frame will be created.
74    * 
75    * @param af
76    * @param createNewFrame
77    */
78   public HMMAlignThread(AlignFrame af, boolean createNewFrame)
79   {
80     this.af = af;
81     alignment = af.getViewport().getAlignment();
82     if (alignment.getDataset() != null)
83     {
84       dataset = alignment.getDataset();
85     }
86     newFrame = createNewFrame;
87     featureSettings = af.getFeatureRenderer().getSettings();
88   }
89
90   /**
91    * Runs the HMMAlignThread: the data on the alignment or group is exported,
92    * then the command is executed in the command line and then the data is
93    * imported and displayed in a new frame (if true). The command is executed
94    * for each segemtn of the alignment.
95    */
96   @Override
97   public void run()
98   {
99     if (af.getSelectedHMM() == null)
100     {
101       JOptionPane.showMessageDialog(af,
102               MessageManager.getString("warn.no_selected_hmm"));
103       return;
104     }
105     else
106     {
107       hmm = af.getSelectedHMM();
108     }
109     barID = System.currentTimeMillis();
110     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
111             barID);
112     cmds.HMMERFOLDER = Cache.getProperty(Preferences.HMMER_PATH);
113
114     // if (!alignment.isAligned())
115     // {
116     // alignment.padGaps();
117     // }
118     prepareAlignment();
119     SequenceI[][] subAlignments = msa.getVisibleContigs('-');
120     allOrders = new ArrayList<>();
121     allResults = new SequenceI[subAlignments.length][];
122     int job = 0;
123     for (SequenceI[] seqs : subAlignments)
124     {
125       cmds.uniquifySequences(seqs);
126       try
127       {
128         createTemporaryFiles();
129       } catch (IOException e2)
130       {
131         e2.printStackTrace();
132       }
133       try
134       {
135         cmds.exportData(seqs, outTemp.getAbsoluteFile(), hmm,
136                 hmmTemp.getAbsoluteFile());
137       } catch (IOException e1)
138       {
139         e1.printStackTrace();
140       }
141       try
142       {
143         boolean ran = runCommand();
144         if (!ran)
145         {
146           JvOptionPane.showInternalMessageDialog(af,
147                   MessageManager.getString("warn.hmmalign_failed"));
148           return;
149         }
150       } catch (IOException | InterruptedException e)
151       {
152         e.printStackTrace();
153       }
154       try
155       {
156         importData(job);
157       } catch (IOException | InterruptedException e)
158       {
159         // TODO Auto-generated catch block
160         e.printStackTrace();
161       }
162       job++;
163     }
164
165     displayResults(newFrame);
166
167     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
168             barID);
169
170   }
171
172   /**
173    * Creates temporary files for exporting and importing the data.
174    * 
175    * @throws IOException
176    */
177   private void createTemporaryFiles() throws IOException
178   {
179     hmmTemp = File.createTempFile("hmm", ".hmm");
180     hmmTemp.deleteOnExit();
181     outTemp = File.createTempFile("output", ".sto");
182     outTemp.deleteOnExit();
183     inputTemp = File.createTempFile("input", ".sto");
184     inputTemp.deleteOnExit();
185   }
186
187   /**
188    * Executes the hmmalign command in the command line.
189    * 
190    * @return
191    * @throws IOException
192    * @throws InterruptedException
193    */
194   private boolean runCommand() throws IOException, InterruptedException
195   {
196     File file = new File(cmds.HMMERFOLDER + "/hmmalign");
197     if (!file.canExecute())
198     {
199       file = new File(cmds.HMMERFOLDER + "/hmmalign.exe");
200       {
201         if (!file.canExecute())
202         {
203           return false;
204         }
205       }
206     }
207     String command = cmds.HMMERFOLDER + cmds.HMMALIGN;
208     String version = Cache.getProperty("HMMER_VERSION");
209     if (!"3.1b2".equals(version))
210     {
211       command += cmds.ALLCOL;
212     }
213     boolean trim = true;
214     String bool = Cache.getProperty("TRIM_TERMINI");
215     if ("false".equals(bool))
216     {
217       trim = false;
218     }
219     if (trim)
220     {
221       command += cmds.TRIM;
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.
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,
273             arrOrders, '-');
274     SequenceI[] alignment = (SequenceI[]) newview[0];
275     HiddenColumns hidden = (HiddenColumns) newview[1];
276     Alignment al = new Alignment(alignment);
277     al.setProperty("Alignment Program", "hmmalign");
278     if (dataset != null)
279     {
280       al.setDataset(dataset);
281     }
282
283     if (newFrame)
284     {
285       displayInNewFrame(al, allOrders, hidden);
286     }
287   }
288
289   /**
290    * Displays the results in a new frame.
291    * 
292    * @param al
293    *          The alignment containing the results.
294    * @param alorders
295    *          The order of the sequences in the alignment on which the jobs were
296    *          run.
297    * @param hidden
298    *          Hidden columns in the previous alignment.
299    */
300   private void displayInNewFrame(AlignmentI al,
301           List<AlignmentOrder> alorders, HiddenColumns hidden)
302   {
303     AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
304             AlignFrame.DEFAULT_HEIGHT);
305
306     // initialise with same renderer settings as in parent alignframe.
307     af.getFeatureRenderer().transferSettings(this.featureSettings);
308
309     if (allOrders.size() > 0)
310     {
311       addSortByMenuItems(af, allOrders);
312     }
313
314     // TODO: refactor retrieve and show as new splitFrame as Desktop method
315
316     /*
317      * If alignment was requested from one half of a SplitFrame, show in a
318      * SplitFrame with the other pane similarly aligned.
319      */
320     AlignFrame requestedBy = this.af;
321     if (requestedBy != null && requestedBy.getSplitViewContainer() != null
322             && requestedBy.getSplitViewContainer()
323                     .getComplement(requestedBy) != null)
324     {
325       AlignmentI complement = requestedBy.getSplitViewContainer()
326               .getComplement(requestedBy);
327       String complementTitle = requestedBy.getSplitViewContainer()
328               .getComplementTitle(requestedBy);
329       // becomes null if the alignment window was closed before the alignment
330       // job finished.
331       AlignmentI copyComplement = new Alignment(complement);
332       // todo should this be done by copy constructor?
333       copyComplement.setGapCharacter(complement.getGapCharacter());
334       // share the same dataset (and the mappings it holds)
335       copyComplement.setDataset(complement.getDataset());
336       copyComplement.alignAs(al);
337       if (copyComplement.getHeight() > 0)
338       {
339         af.setTitle(this.af.getTitle());
340         AlignFrame af2 = new AlignFrame(copyComplement,
341                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
342         af2.setTitle(complementTitle);
343         String linkedTitle = MessageManager
344                 .getString("label.linked_view_title");
345         JInternalFrame splitFrame = new SplitFrame(
346                 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
347         Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
348         return;
349       }
350     }
351
352     /*
353      * Not from SplitFrame, or failed to created a complementary alignment
354      */
355     Desktop.addInternalFrame(af, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
356             AlignFrame.DEFAULT_HEIGHT);
357   }
358
359   /**
360    * Add sort order options to the AlignFrame menus.
361    * 
362    * @param af
363    * @param alorders
364    */
365   protected void addSortByMenuItems(AlignFrame af,
366           List<AlignmentOrder> alorders)
367   {
368     // update orders
369     if (alorders.size() == 1)
370     {
371       af.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
372     }
373     else
374     {
375       // construct a non-redundant ordering set
376       List<String> names = new ArrayList<>();
377       for (int i = 0, l = alorders.size(); i < l; i++)
378       {
379         String orderName = " Region " + i;
380         int j = i + 1;
381
382         while (j < l)
383         {
384           if (alorders.get(i).equals(alorders.get(j)))
385           {
386             alorders.remove(j);
387             l--;
388             orderName += "," + j;
389           }
390           else
391           {
392             j++;
393           }
394         }
395
396         if (i == 0 && j == 1)
397         {
398           names.add("");
399         }
400         else
401         {
402           names.add(orderName);
403         }
404       }
405       for (int i = 0, l = alorders.size(); i < l; i++)
406       {
407         af.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering",
408                 alorders.get(i));
409       }
410     }
411   }
412
413   /**
414    * Runs hmmalign, and waits for the results to be imported before continuing
415    */
416   public void hmmalignWaitTillComplete()
417   {
418     Thread loader = new Thread(this);
419     loader.start();
420
421     while (loader.isAlive())
422     {
423       try
424       {
425         Thread.sleep(500);
426       } catch (Exception ex)
427       {
428       }
429     }
430
431   }
432   }
433
434