package jalview.hmmer; import jalview.analysis.AlignmentSorter; 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.SplitFrame; import jalview.io.DataSourceType; import jalview.io.StockholmFile; import jalview.util.FileUtils; 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.Hashtable; import java.util.List; import javax.swing.JInternalFrame; public class HMMAlign extends HmmerCommand { static final String HMMALIGN = "hmmalign"; static final String ARG_TRIM = "--trim"; private final AlignmentI dataset; /** * Constructor for the HMMAlignThread * * @param af * @param args */ public HMMAlign(AlignFrame af, List args) { super(af, args); if (alignment.getDataset() != null) { dataset = alignment.getDataset(); } else { dataset = null; } } /** * 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 segment of the alignment. Call this method directly to execute * synchronously, or via start() in a new Thread for asynchronously. */ @Override public void run() { HiddenMarkovModel hmm = getHmmProfile(); long msgId = System.currentTimeMillis(); af.setProgressBar(MessageManager.getString("status.running_hmmalign"), msgId); AlignmentView msa = af.gatherSequencesForAlignment(); SequenceI[][] subAlignments = msa.getVisibleContigs(alignment.getGapCharacter()); List allOrders = new ArrayList<>(); SequenceI[][] allResults = new SequenceI[subAlignments.length][]; int job = 0; for (SequenceI[] seqs : subAlignments) { Hashtable sequencesHash = stashSequences(seqs); try { File modelFile = FileUtils.createTempFile("hmm", ".hmm"); File alignmentFile = FileUtils.createTempFile("output", ".sto"); File resultFile = FileUtils.createTempFile("input", ".sto"); exportStockholm(seqs, alignmentFile.getAbsoluteFile(), null); exportHmm(hmm, modelFile.getAbsoluteFile()); boolean ran = runCommand(modelFile, alignmentFile, resultFile); if (!ran) { JvOptionPane.showInternalMessageDialog(af, MessageManager .formatMessage("warn.command_failed", "hmmalign")); return; } SequenceI[] result = importData(resultFile, allOrders); recoverSequences(sequencesHash, result); allResults[job] = result; modelFile.delete(); alignmentFile.delete(); resultFile.delete(); } catch (IOException e) { e.printStackTrace(); } job++; } String title = "hmmalign to " + hmm.getConsensusSequence().getName(); displayResults(allResults, allOrders, msa, title); af.setProgressBar("", msgId); } /** * Executes the hmmalign command and returns true if successful, false if an * error is detected * * @param modelFile * the HMM to align to * @param alignmentFile * the sequences to align * @param resultFile * the file to hold the results of alignment * @return * @throws IOException */ private boolean runCommand(File modelFile, File alignmentFile, File resultFile) throws IOException { String command = getCommandPath(HMMALIGN); if (command == null) { return false; } List args = new ArrayList<>(); args.add(command); if (params != null) { for (ArgumentI arg : params) { String name = arg.getName(); if (MessageManager.getString("label.trim_termini").equals(name)) { args.add(ARG_TRIM); } } } args.add("-o"); args.add(getFilePath(resultFile, true)); args.add(getFilePath(modelFile, true)); args.add(getFilePath(alignmentFile, true)); return runCommand(args); } /** * Imports the data from the file holding the output of hmmalign * * @param resultFile * @param allOrders * a list of alignment orders to add to * * @return * @throws IOException */ private SequenceI[] importData(File resultFile, List allOrders) throws IOException { StockholmFile file = new StockholmFile(getFilePath(resultFile, false), DataSourceType.FILE); SequenceI[] result = file.getSeqsAsArray(); AlignmentOrder msaorder = new AlignmentOrder(result); AlignmentSorter.recoverOrder(result); allOrders.add(msaorder); return result; } /** * Displays the results of all 'jobs' in a new frame * * @param allResults * * @param allOrders * @param msa * @param title */ private void displayResults(SequenceI[][] allResults, List allOrders, AlignmentView msa, String title) { AlignmentOrder[] arrOrders = allOrders .toArray(new AlignmentOrder[allOrders.size()]); Object[] newview = msa.getUpdatedView(allResults, arrOrders, alignment.getGapCharacter()); SequenceI[] seqs = (SequenceI[]) newview[0]; HiddenColumns hidden = (HiddenColumns) newview[1]; Alignment al = new Alignment(seqs); al.setProperty("Alignment Program", "hmmalign"); if (dataset != null) { al.setDataset(dataset); } displayInNewFrame(al, allOrders, hidden, title); } /** * 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 * @param title */ private void displayInNewFrame(AlignmentI al, List alorders, HiddenColumns hidden, String title) { AlignFrame alignFrame = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); alignFrame.setTitle(title); FeatureRendererSettings featureSettings = af.getFeatureRenderer() .getSettings(); // initialise with same renderer settings as in parent alignframe. alignFrame.getFeatureRenderer().transferSettings(featureSettings); addSortByMenuItems(alignFrame, alorders); // 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) { alignFrame.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() ? alignFrame : af2, al.isNucleotide() ? af2 : alignFrame); Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1); return; } } /* * Not from SplitFrame, or failed to created a complementary alignment */ Desktop.addInternalFrame(alignFrame, alignFrame.getTitle(), AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } /** * Adds sort order options to the AlignFrame menus * * @param alignFrame * @param alorders */ protected void addSortByMenuItems(AlignFrame alignFrame, List alorders) { // update orders if (alorders.size() == 1) { alignFrame.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++) { alignFrame.addSortByOrderMenuItem("hmmalign" + (names.get(i)) + " Ordering", alorders.get(i)); } } } }