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