JAL-2629 add validation for HMMER path
[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   HMMERCommands cmds = new HMMERCommands();
39
40   AlignFrame af;
41
42   AlignmentI alignment;
43
44   AlignmentI dataset;
45
46   List<AlignmentOrder> orders;
47
48   AlignmentView msa;
49
50   HiddenMarkovModel hmm;
51
52   boolean newFrame;
53
54   long barID;
55
56   Map<Integer, SequenceI> hmmSeqs;
57
58   File hmmTemp = null;
59
60   File outTemp = null;
61
62   File inputTemp = null;
63
64   List<AlignmentOrder> allOrders;
65
66   SequenceI[][] allResults;
67
68   public HMMAlignThread(AlignFrame af, boolean createNewFrame)
69   {
70     this.af = af;
71     alignment = af.getViewport().getAlignment();
72     if (alignment.getDataset() != null)
73     {
74       dataset = alignment.getDataset();
75     }
76     newFrame = createNewFrame;
77     featureSettings = af.getFeatureRenderer().getSettings();
78   }
79
80   @Override
81   public void run()
82   {
83     if (af.getViewport().getSelectedHMM() == null)
84     {
85       JOptionPane.showMessageDialog(af,
86               MessageManager.getString("warn.no_selected_hmm"));
87       return;
88     }
89     else
90     {
91       hmm = af.getViewport().getSelectedHMM();
92     }
93     barID = System.currentTimeMillis();
94     af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
95             barID);
96     cmds.HMMERFOLDER = Cache.getProperty(Preferences.HMMER_PATH);
97
98     // if (!alignment.isAligned())
99     // {
100     // alignment.padGaps();
101     // }
102     prepareAlignment();
103     SequenceI[][] subAlignments = msa.getVisibleContigs('-');
104     allOrders = new ArrayList<>();
105     allResults = new SequenceI[subAlignments.length][];
106     int job = 0;
107     for (SequenceI[] seqs : subAlignments)
108     {
109       cmds.uniquifySequences(seqs);
110       try
111       {
112         createTemporaryFiles();
113       } catch (IOException e2)
114       {
115         e2.printStackTrace();
116       }
117       try
118       {
119         cmds.exportData(seqs, outTemp.getAbsoluteFile(), hmm,
120                 hmmTemp.getAbsoluteFile());
121       } catch (IOException e1)
122       {
123         e1.printStackTrace();
124       }
125       try
126       {
127         boolean ran = runCommand();
128         if (!ran)
129         {
130           JvOptionPane.showInternalMessageDialog(af,
131                   MessageManager.getString("warn.hmmalign_failed"));
132           return;
133         }
134       } catch (IOException | InterruptedException e)
135       {
136         e.printStackTrace();
137       }
138       try
139       {
140         importData(job);
141       } catch (IOException | InterruptedException e)
142       {
143         // TODO Auto-generated catch block
144         e.printStackTrace();
145       }
146       job++;
147     }
148
149     displayResults(newFrame);
150
151     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
152             barID);
153
154   }
155
156   private void createTemporaryFiles() throws IOException
157   {
158     hmmTemp = File.createTempFile("hmm", ".hmm");
159     hmmTemp.deleteOnExit();
160     outTemp = File.createTempFile("output", ".sto");
161     outTemp.deleteOnExit();
162     inputTemp = File.createTempFile("input", ".sto");
163     inputTemp.deleteOnExit();
164   }
165
166   private boolean runCommand() throws IOException, InterruptedException
167   {
168     File file = new File(cmds.HMMERFOLDER + "/binaries/hmmalign.exe");
169     if (!file.canExecute())
170     {
171       return false;
172     }
173     String command = cmds.HMMERFOLDER + cmds.HMMALIGN;
174     if (!hmm.getFileHeader().contains("HMMER3/f"))
175     {
176       command += cmds.ALLCOL;
177     }
178     boolean trim = true;
179     String bool = Cache.getProperty("TRIM_TERMINI");
180     if ("false".equals(bool))
181     {
182       trim = false;
183     }
184     if (trim)
185     {
186       command += cmds.TRIM;
187     }
188     command += " -o" + inputTemp.getAbsolutePath() + cmds.SPACE
189             + hmmTemp.getAbsolutePath() + cmds.SPACE
190             + outTemp.getAbsolutePath();
191     return cmds.runCommand(command);
192   }
193
194   private void importData(int index)
195           throws IOException, InterruptedException
196   {
197     StockholmFile file = new StockholmFile(inputTemp.getAbsolutePath(),
198             DataSourceType.FILE);
199     SequenceI[] result = file.getSeqsAsArray();
200     AlignmentOrder msaorder = new AlignmentOrder(result);
201     // always recover the order - makes parseResult()'s life easier.
202     jalview.analysis.AlignmentSorter.recoverOrder(result);
203     jalview.analysis.SeqsetUtils.deuniquify(cmds.hash, result);
204     allOrders.add(msaorder);
205     allResults[index] = result;
206
207     /*
208     if (newFrame)
209     {
210       FileLoader loader = new FileLoader();
211       AlignFrame aFrame = new AlignFrame(new Alignment(new SequenceI[1]),
212               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
213       Desktop.addInternalFrame(aFrame, aFrame.getTitle(),
214               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
215       aFrame.setTitle(
216               af.getName() + "Aligned to " + hmm.getName() + "'s HMM");
217       af.getViewport().setAlignment(null);
218     
219       aFrame.loadJalviewDataFile(inputTemp.getAbsolutePath(),
220               DataSourceType.FILE, FileFormat.Stockholm, null);
221     
222     
223     
224       Map<Integer, SequenceI> copy = new HashMap<>(
225               hmmSeqs);
226       addSeqs(aFrame, copy);
227       SequenceI seq = aFrame.getViewport().getAlignment()
228               .getSequenceAt(0);
229       seq.getHMM().mapToReferenceAnnotation(aFrame, seq);
230       addSeqs(af, hmmSeqs);
231     }
232     else
233     {
234       af.getViewport().getAlignment().getSequences().clear();
235       af.setIsRecurring(true);
236       af.loadJalviewDataFile(inputTemp.getAbsolutePath(),
237               DataSourceType.FILE, FileFormat.Stockholm, null);
238       af.setIsRecurring(false);
239       addSeqs(af, hmmSeqs);
240     }
241     */
242     hmmTemp.delete();
243     outTemp.delete();
244     inputTemp.delete();
245   }
246
247   private void prepareAlignment()
248   {
249     // hmmSeqs = alignment.getHMMConsensusSequences(true);
250     msa = af.gatherSequencesForAlignment();
251   }
252
253   private void displayResults(boolean newFrame)
254   {
255     AlignmentOrder[] arrOrders = allOrders
256             .toArray(new AlignmentOrder[allOrders.size()]);
257     Object[] newview = msa.getUpdatedView(allResults,
258             arrOrders, '-');
259     SequenceI[] alignment = (SequenceI[]) newview[0];
260     HiddenColumns hidden = (HiddenColumns) newview[1];
261     Alignment al = new Alignment(alignment);
262     al.setProperty("Alignment Program", "hmmalign");
263     if (dataset != null)
264     {
265       al.setDataset(dataset);
266     }
267
268     if (newFrame)
269     {
270       displayInNewFrame(al, allOrders, hidden);
271     }
272   }
273
274   private void displayInNewFrame(AlignmentI al,
275           List<AlignmentOrder> alorders, HiddenColumns hidden)
276   {
277     AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
278             AlignFrame.DEFAULT_HEIGHT);
279
280     // initialise with same renderer settings as in parent alignframe.
281     af.getFeatureRenderer().transferSettings(this.featureSettings);
282
283     if (allOrders.size() > 0)
284     {
285       addSortByMenuItems(af, allOrders);
286     }
287
288     // TODO: refactor retrieve and show as new splitFrame as Desktop method
289
290     /*
291      * If alignment was requested from one half of a SplitFrame, show in a
292      * SplitFrame with the other pane similarly aligned.
293      */
294     AlignFrame requestedBy = this.af;
295     if (requestedBy != null && requestedBy.getSplitViewContainer() != null
296             && requestedBy.getSplitViewContainer()
297                     .getComplement(requestedBy) != null)
298     {
299       AlignmentI complement = requestedBy.getSplitViewContainer()
300               .getComplement(requestedBy);
301       String complementTitle = requestedBy.getSplitViewContainer()
302               .getComplementTitle(requestedBy);
303       // becomes null if the alignment window was closed before the alignment
304       // job finished.
305       AlignmentI copyComplement = new Alignment(complement);
306       // todo should this be done by copy constructor?
307       copyComplement.setGapCharacter(complement.getGapCharacter());
308       // share the same dataset (and the mappings it holds)
309       copyComplement.setDataset(complement.getDataset());
310       copyComplement.alignAs(al);
311       if (copyComplement.getHeight() > 0)
312       {
313         af.setTitle(this.af.getTitle());
314         AlignFrame af2 = new AlignFrame(copyComplement,
315                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
316         af2.setTitle(complementTitle);
317         String linkedTitle = MessageManager
318                 .getString("label.linked_view_title");
319         JInternalFrame splitFrame = new SplitFrame(
320                 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
321         Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
322         return;
323       }
324     }
325
326     /*
327      * Not from SplitFrame, or failed to created a complementary alignment
328      */
329     Desktop.addInternalFrame(af, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
330             AlignFrame.DEFAULT_HEIGHT);
331   }
332
333   /**
334    * Add sort order options to the AlignFrame menus.
335    * 
336    * @param af
337    * @param alorders
338    */
339   protected void addSortByMenuItems(AlignFrame af,
340           List<AlignmentOrder> alorders)
341   {
342     // update orders
343     if (alorders.size() == 1)
344     {
345       af.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
346     }
347     else
348     {
349       // construct a non-redundant ordering set
350       List<String> names = new ArrayList<>();
351       for (int i = 0, l = alorders.size(); i < l; i++)
352       {
353         String orderName = " Region " + i;
354         int j = i + 1;
355
356         while (j < l)
357         {
358           if (alorders.get(i).equals(alorders.get(j)))
359           {
360             alorders.remove(j);
361             l--;
362             orderName += "," + j;
363           }
364           else
365           {
366             j++;
367           }
368         }
369
370         if (i == 0 && j == 1)
371         {
372           names.add("");
373         }
374         else
375         {
376           names.add(orderName);
377         }
378       }
379       for (int i = 0, l = alorders.size(); i < l; i++)
380       {
381         af.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering",
382                 alorders.get(i));
383       }
384     }
385   }
386
387   }
388
389