JAL-2629 simplifying method signatures in Hmmer command classes
[jalview.git] / src / jalview / hmmer / HMMAlignThread.java
1 package jalview.hmmer;
2
3 import jalview.analysis.AlignmentSorter;
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.SplitFrame;
15 import jalview.io.DataSourceType;
16 import jalview.io.StockholmFile;
17 import jalview.util.MessageManager;
18 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
19 import jalview.ws.params.ArgumentI;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.swing.JInternalFrame;
27
28 public class HMMAlignThread extends HmmerCommand implements Runnable
29 {
30   static final String HMMALIGN = "hmmalign";
31
32   static final String ARG_TRIM = "--trim";
33
34   /*
35    * feature settings from view that job was associated with
36    */
37   protected FeatureRendererSettings featureSettings = null;
38
39   AlignmentI alignment;
40
41   AlignmentI dataset;
42
43   List<AlignmentOrder> orders;
44
45   AlignmentView msa;
46
47   HiddenMarkovModel hmm;
48
49   SequenceI[][] allResults;
50
51   /**
52    * Constructor for the HMMAlignThread
53    * 
54    * @param af
55    * @param args
56    */
57   public HMMAlignThread(AlignFrame af, List<ArgumentI> args)
58   {
59     super(af, args);
60     alignment = af.getViewport().getAlignment();
61     if (alignment.getDataset() != null)
62     {
63       dataset = alignment.getDataset();
64     }
65     featureSettings = af.getFeatureRenderer().getSettings();
66   }
67
68   /**
69    * Runs the HMMAlignThread: the data on the alignment or group is exported,
70    * then the command is executed in the command line and then the data is
71    * imported and displayed in a new frame (if true). The command is executed
72    * for each segemtn of the alignment.
73    */
74   @Override
75   public void run()
76   {
77     hmm = af.getSelectedHMM();
78
79     long msgId = System.currentTimeMillis();
80     af.setProgressBar(MessageManager.getString("status.running_hmmalign"),
81             msgId);
82     prepareAlignment();
83     SequenceI[][] subAlignments = msa.getVisibleContigs('-');
84
85     List<AlignmentOrder> allOrders = new ArrayList<>();
86     allResults = new SequenceI[subAlignments.length][];
87     int job = 0;
88     for (SequenceI[] seqs : subAlignments)
89     {
90       stashSequences(seqs);
91       try
92       {
93         File modelFile = createTempFile("hmm", ".hmm");
94         File alignmentFile = createTempFile("output", ".sto");
95         File resultFile = createTempFile("input", ".sto");
96
97         exportStockholm(seqs, alignmentFile.getAbsoluteFile(), null);
98         exportHmm(hmm, modelFile.getAbsoluteFile());
99
100         boolean ran = runCommand(modelFile, alignmentFile, resultFile);
101         if (!ran)
102         {
103           JvOptionPane.showInternalMessageDialog(af,
104                   MessageManager.getString("warn.hmmalign_failed"));
105           return;
106         }
107
108         SequenceI[] result = importData(resultFile, allOrders);
109         allResults[job] = result;
110         modelFile.delete();
111         alignmentFile.delete();
112         resultFile.delete();
113       } catch (IOException e)
114       {
115         e.printStackTrace();
116       }
117       job++;
118     }
119
120     displayResults(allOrders);
121
122     af.setProgressBar("", msgId);
123   }
124
125   /**
126    * Executes the hmmalign command and returns true if successful, false if an
127    * error is detected
128    * 
129    * @param modelFile
130    *          the HMM to align to
131    * @param alignmentFile
132    *          the sequences to align
133    * @param resultFile
134    *          the file to hold the results of alignment
135    * @return
136    * @throws IOException
137    */
138   private boolean runCommand(File modelFile, File alignmentFile,
139           File resultFile)
140           throws IOException
141   {
142     String command = getCommandPath(HMMALIGN);
143     if (command == null)
144     {
145       return false;
146     }
147     List<String> args = new ArrayList<>();
148     args.add(command);
149
150     if (params != null)
151     {
152       for (ArgumentI arg : params)
153       {
154         String name = arg.getName();
155         if (MessageManager.getString("label.trim_termini").equals(name))
156         {
157           args.add(ARG_TRIM);
158         }
159       }
160     }
161     args.add("-o");
162     args.add(resultFile.getAbsolutePath());
163     args.add(modelFile.getAbsolutePath());
164     args.add(alignmentFile.getAbsolutePath());
165     
166     return runCommand(args);
167   }
168
169   /**
170    * Imports the data from the file holding the output of hmmalign
171    * 
172    * @param resultFile
173    * @param allOrders
174    *          a list of alignment orders to add to
175    * 
176    * @return
177    * @throws IOException
178    */
179   private SequenceI[] importData(File resultFile,
180           List<AlignmentOrder> allOrders) throws IOException
181   {
182     StockholmFile file = new StockholmFile(resultFile.getAbsolutePath(),
183             DataSourceType.FILE);
184     SequenceI[] result = file.getSeqsAsArray();
185     AlignmentOrder msaorder = new AlignmentOrder(result);
186     AlignmentSorter.recoverOrder(result);
187     recoverSequences(result);
188     allOrders.add(msaorder);
189
190     return result;
191   }
192
193   /**
194    * Gathers the sequences in preparation for the alignment.
195    */
196   private void prepareAlignment()
197   {
198     msa = af.gatherSequencesForAlignment();
199   }
200
201   /**
202    * Displays the results of all 'jobs' in a new frame
203    * 
204    * @param allOrders
205    */
206   private void displayResults(List<AlignmentOrder> allOrders)
207   {
208     AlignmentOrder[] arrOrders = allOrders
209             .toArray(new AlignmentOrder[allOrders.size()]);
210     Object[] newview = msa.getUpdatedView(allResults, arrOrders, '-');
211     SequenceI[] seqs = (SequenceI[]) newview[0];
212     HiddenColumns hidden = (HiddenColumns) newview[1];
213     Alignment al = new Alignment(seqs);
214     al.setProperty("Alignment Program", "hmmalign");
215     if (dataset != null)
216     {
217       al.setDataset(dataset);
218     }
219
220     displayInNewFrame(al, allOrders, hidden);
221   }
222
223   /**
224    * Displays the results in a new frame
225    * 
226    * @param al
227    *          The alignment containing the results
228    * @param alorders
229    *          The order of the sequences in the alignment on which the jobs were
230    *          run
231    * @param hidden
232    *          Hidden columns in the previous alignment
233    */
234   private void displayInNewFrame(AlignmentI al,
235           List<AlignmentOrder> alorders, HiddenColumns hidden)
236   {
237     AlignFrame alignFrame = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
238             AlignFrame.DEFAULT_HEIGHT);
239
240     // initialise with same renderer settings as in parent alignframe.
241     alignFrame.getFeatureRenderer().transferSettings(this.featureSettings);
242
243     addSortByMenuItems(alignFrame, alorders);
244
245     // TODO: refactor retrieve and show as new splitFrame as Desktop method
246
247     /*
248      * If alignment was requested from one half of a SplitFrame, show in a
249      * SplitFrame with the other pane similarly aligned.
250      */
251     AlignFrame requestedBy = this.af;
252     if (requestedBy != null && requestedBy.getSplitViewContainer() != null
253             && requestedBy.getSplitViewContainer()
254                     .getComplement(requestedBy) != null)
255     {
256       AlignmentI complement = requestedBy.getSplitViewContainer()
257               .getComplement(requestedBy);
258       String complementTitle = requestedBy.getSplitViewContainer()
259               .getComplementTitle(requestedBy);
260       // becomes null if the alignment window was closed before the alignment
261       // job finished.
262       AlignmentI copyComplement = new Alignment(complement);
263       // todo should this be done by copy constructor?
264       copyComplement.setGapCharacter(complement.getGapCharacter());
265       // share the same dataset (and the mappings it holds)
266       copyComplement.setDataset(complement.getDataset());
267       copyComplement.alignAs(al);
268       if (copyComplement.getHeight() > 0)
269       {
270         alignFrame.setTitle(this.af.getTitle());
271         AlignFrame af2 = new AlignFrame(copyComplement,
272                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
273         af2.setTitle(complementTitle);
274         String linkedTitle = MessageManager
275                 .getString("label.linked_view_title");
276         JInternalFrame splitFrame = new SplitFrame(
277                 al.isNucleotide() ? alignFrame : af2, al.isNucleotide() ? af2 : alignFrame);
278         Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
279         return;
280       }
281     }
282
283     /*
284      * Not from SplitFrame, or failed to created a complementary alignment
285      */
286     Desktop.addInternalFrame(alignFrame, alignFrame.getTitle(), AlignFrame.DEFAULT_WIDTH,
287             AlignFrame.DEFAULT_HEIGHT);
288   }
289
290   /**
291    * Adds sort order options to the AlignFrame menus
292    * 
293    * @param alignFrame
294    * @param alorders
295    */
296   protected void addSortByMenuItems(AlignFrame alignFrame,
297           List<AlignmentOrder> alorders)
298   {
299     // update orders
300     if (alorders.size() == 1)
301     {
302       alignFrame.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
303     }
304     else
305     {
306       // construct a non-redundant ordering set
307       List<String> names = new ArrayList<>();
308       for (int i = 0, l = alorders.size(); i < l; i++)
309       {
310         String orderName = " Region " + i;
311         int j = i + 1;
312
313         while (j < l)
314         {
315           if (alorders.get(i).equals(alorders.get(j)))
316           {
317             alorders.remove(j);
318             l--;
319             orderName += "," + j;
320           }
321           else
322           {
323             j++;
324           }
325         }
326
327         if (i == 0 && j == 1)
328         {
329           names.add("");
330         }
331         else
332         {
333           names.add(orderName);
334         }
335       }
336       for (int i = 0, l = alorders.size(); i < l; i++)
337       {
338         alignFrame.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering",
339                 alorders.get(i));
340       }
341     }
342   }
343
344   /**
345    * Runs hmmalign, and waits for the results to be imported before continuing
346    */
347   public void hmmalignWaitTillComplete()
348   {
349     Thread loader = new Thread(this);
350     loader.start();
351
352     while (loader.isAlive())
353     {
354       try
355       {
356         Thread.sleep(500);
357       } catch (Exception ex)
358       {
359       }
360     }
361
362   }
363 }