package jalview.hmmer; import jalview.bin.Cache; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.AlignmentView; import jalview.datamodel.HiddenColumns; import jalview.datamodel.HiddenMarkovModel; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.gui.Desktop; import jalview.gui.JvOptionPane; import jalview.gui.Preferences; import jalview.gui.SplitFrame; import jalview.io.DataSourceType; import jalview.io.StockholmFile; import jalview.util.MessageManager; import jalview.viewmodel.seqfeatures.FeatureRendererSettings; import jalview.ws.params.ArgumentI; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.swing.JInternalFrame; public class HMMAlignThread implements Runnable { /** * feature settings from view that job was associated with */ protected FeatureRendererSettings featureSettings = null; /** * Object containing frequently used commands. */ HMMERCommands cmds = new HMMERCommands(); AlignFrame af; AlignmentI alignment; AlignmentI dataset; List orders; AlignmentView msa; HiddenMarkovModel hmm; List args; boolean newFrame; long barID; Map hmmSeqs; File hmmTemp = null; File outTemp = null; File inputTemp = null; List allOrders; SequenceI[][] allResults; /** * Constructor for the HMMAlignThread. If create new frame is set to true, a * new frame will be created. * * @param af * @param createNewFrame */ public HMMAlignThread(AlignFrame af, boolean createNewFrame, List args) { this.af = af; alignment = af.getViewport().getAlignment(); if (alignment.getDataset() != null) { dataset = alignment.getDataset(); } newFrame = createNewFrame; featureSettings = af.getFeatureRenderer().getSettings(); this.args = args; } /** * Runs the HMMAlignThread: the data on the alignment or group is exported, * then the command is executed in the command line and then the data is * imported and displayed in a new frame (if true). The command is executed * for each segemtn of the alignment. */ @Override public void run() { hmm = af.getSelectedHMM(); barID = System.currentTimeMillis(); af.setProgressBar(MessageManager.getString("status.running_hmmalign"), barID); cmds.HMMERFOLDER = Cache.getProperty(Preferences.HMMER_PATH); prepareAlignment(); SequenceI[][] subAlignments = msa.getVisibleContigs('-'); allOrders = new ArrayList<>(); allResults = new SequenceI[subAlignments.length][]; int job = 0; for (SequenceI[] seqs : subAlignments) { cmds.uniquifySequences(seqs); try { createTemporaryFiles(); } catch (IOException e2) { e2.printStackTrace(); } try { cmds.exportData(seqs, outTemp.getAbsoluteFile(), hmm, hmmTemp.getAbsoluteFile()); } catch (IOException e1) { e1.printStackTrace(); } try { boolean ran = runCommand(); if (!ran) { JvOptionPane.showInternalMessageDialog(af, MessageManager.getString("warn.hmmalign_failed")); return; } } catch (IOException | InterruptedException e) { e.printStackTrace(); } try { importData(job); } catch (IOException | InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } job++; } displayResults(newFrame); af.setProgressBar(MessageManager.getString("status.running_hmmalign"), barID); } /** * Creates temporary files for exporting and importing the data. * * @throws IOException */ private void createTemporaryFiles() throws IOException { if (hmmTemp == null) { hmmTemp = File.createTempFile("hmm", ".hmm"); hmmTemp.deleteOnExit(); } if (outTemp == null) { outTemp = File.createTempFile("output", ".sto"); outTemp.deleteOnExit(); } inputTemp = File.createTempFile("input", ".sto"); inputTemp.deleteOnExit(); } /** * Executes the hmmalign command in the command line. * * @return * @throws IOException * @throws InterruptedException */ private boolean runCommand() throws IOException, InterruptedException { File file = new File(cmds.HMMERFOLDER + "/hmmalign"); if (!file.canExecute()) { file = new File(cmds.HMMERFOLDER + "/hmmalign.exe"); { if (!file.canExecute()) { return false; } } } String command = cmds.HMMERFOLDER + cmds.HMMALIGN; String version = Cache.getProperty("HMMER_VERSION"); if (!"3.1b2".equals(version)) { command += cmds.ALLCOL; } if (args != null) { for (ArgumentI arg : args) { String name = arg.getName(); switch (name) { case "Trim Non-Matching Termini": command += "--trim"; } } } command += " -o " + inputTemp.getAbsolutePath() + cmds.SPACE + hmmTemp.getAbsolutePath() + cmds.SPACE + outTemp.getAbsolutePath(); return cmds.runCommand(command); } /** * Imports the data from the temporary file to which the output of hmmalign is * directed. this is used for an internal job. * * @param index * The index of the 'job' (or region of an alignment). * @throws IOException * @throws InterruptedException */ private void importData(int index) throws IOException, InterruptedException { StockholmFile file = new StockholmFile(inputTemp.getAbsolutePath(), DataSourceType.FILE); SequenceI[] result = file.getSeqsAsArray(); AlignmentOrder msaorder = new AlignmentOrder(result); jalview.analysis.AlignmentSorter.recoverOrder(result); jalview.analysis.SeqsetUtils.deuniquify(cmds.hash, result); allOrders.add(msaorder); allResults[index] = result; hmmTemp.delete(); outTemp.delete(); inputTemp.delete(); } /** * Gathers the sequences in preparation for the alignment. */ private void prepareAlignment() { // hmmSeqs = alignment.getHMMConsensusSequences(true); msa = af.gatherSequencesForAlignment(); } /** * Displays the results of all 'jobs'. * * @param newFrame */ private void displayResults(boolean newFrame) { AlignmentOrder[] arrOrders = allOrders .toArray(new AlignmentOrder[allOrders.size()]); Object[] newview = msa.getUpdatedView(allResults, arrOrders, '-'); SequenceI[] alignment = (SequenceI[]) newview[0]; HiddenColumns hidden = (HiddenColumns) newview[1]; Alignment al = new Alignment(alignment); al.setProperty("Alignment Program", "hmmalign"); if (dataset != null) { al.setDataset(dataset); } if (newFrame) { displayInNewFrame(al, allOrders, hidden); } } /** * Displays the results in a new frame. * * @param al * The alignment containing the results. * @param alorders * The order of the sequences in the alignment on which the jobs were * run. * @param hidden * Hidden columns in the previous alignment. */ private void displayInNewFrame(AlignmentI al, List alorders, HiddenColumns hidden) { AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); // initialise with same renderer settings as in parent alignframe. af.getFeatureRenderer().transferSettings(this.featureSettings); if (allOrders.size() > 0) { addSortByMenuItems(af, allOrders); } // TODO: refactor retrieve and show as new splitFrame as Desktop method /* * If alignment was requested from one half of a SplitFrame, show in a * SplitFrame with the other pane similarly aligned. */ AlignFrame requestedBy = this.af; if (requestedBy != null && requestedBy.getSplitViewContainer() != null && requestedBy.getSplitViewContainer() .getComplement(requestedBy) != null) { AlignmentI complement = requestedBy.getSplitViewContainer() .getComplement(requestedBy); String complementTitle = requestedBy.getSplitViewContainer() .getComplementTitle(requestedBy); // becomes null if the alignment window was closed before the alignment // job finished. AlignmentI copyComplement = new Alignment(complement); // todo should this be done by copy constructor? copyComplement.setGapCharacter(complement.getGapCharacter()); // share the same dataset (and the mappings it holds) copyComplement.setDataset(complement.getDataset()); copyComplement.alignAs(al); if (copyComplement.getHeight() > 0) { af.setTitle(this.af.getTitle()); AlignFrame af2 = new AlignFrame(copyComplement, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); af2.setTitle(complementTitle); String linkedTitle = MessageManager .getString("label.linked_view_title"); JInternalFrame splitFrame = new SplitFrame( al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af); Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1); return; } } /* * Not from SplitFrame, or failed to created a complementary alignment */ Desktop.addInternalFrame(af, af.getTitle(), AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } /** * Add sort order options to the AlignFrame menus. * * @param af * @param alorders */ protected void addSortByMenuItems(AlignFrame af, List alorders) { // update orders if (alorders.size() == 1) { af.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0)); } else { // construct a non-redundant ordering set List names = new ArrayList<>(); for (int i = 0, l = alorders.size(); i < l; i++) { String orderName = " Region " + i; int j = i + 1; while (j < l) { if (alorders.get(i).equals(alorders.get(j))) { alorders.remove(j); l--; orderName += "," + j; } else { j++; } } if (i == 0 && j == 1) { names.add(""); } else { names.add(orderName); } } for (int i = 0, l = alorders.size(); i < l; i++) { af.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering", alorders.get(i)); } } } /** * Runs hmmalign, and waits for the results to be imported before continuing */ public void hmmalignWaitTillComplete() { Thread loader = new Thread(this); loader.start(); while (loader.isAlive()) { try { Thread.sleep(500); } catch (Exception ex) { } } } }