--- /dev/null
+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<ArgumentI> 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<AlignmentOrder> 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<String> 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<AlignmentOrder> 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<AlignmentOrder> 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<AlignmentOrder> 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<AlignmentOrder> alorders)
+ {
+ // update orders
+ if (alorders.size() == 1)
+ {
+ alignFrame.addSortByOrderMenuItem("hmmalign" + " Ordering", alorders.get(0));
+ }
+ else
+ {
+ // construct a non-redundant ordering set
+ List<String> 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));
+ }
+ }
+ }
+}