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