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