X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fhmmer%2FHMMAlign.java;fp=src%2Fjalview%2Fhmmer%2FHMMAlign.java;h=b5f047e35076cdcb5634365738993cfb36ed2c88;hb=7692386ccfe778075dd83a753d30a7a27fe507be;hp=0000000000000000000000000000000000000000;hpb=97264690f394f98cf13cd1d834a53e8956fb1b0e;p=jalview.git diff --git a/src/jalview/hmmer/HMMAlign.java b/src/jalview/hmmer/HMMAlign.java new file mode 100644 index 0000000..b5f047e --- /dev/null +++ b/src/jalview/hmmer/HMMAlign.java @@ -0,0 +1,337 @@ +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)); + args.add(getFilePath(modelFile)); + args.add(getFilePath(alignmentFile)); + + 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), + 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)); + } + } + } +}