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