JAL-2629 tidy tests, refactor hmmer node mapping slightly
[jalview.git] / src / jalview / hmmer / HMMBuildThread.java
index 4260c34..c52fb86 100644 (file)
@@ -1,7 +1,6 @@
 package jalview.hmmer;
 
-
-import jalview.bin.Cache;
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
@@ -9,207 +8,246 @@ import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.JvOptionPane;
-import jalview.gui.Preferences;
 import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
-import jalview.io.FileLoader;
 import jalview.io.FileParse;
 import jalview.io.HMMFile;
 import jalview.util.MessageManager;
 import jalview.ws.params.ArgumentI;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.swing.JOptionPane;
 
-public class HMMBuildThread implements Runnable
+public class HMMBuildThread extends HmmerCommand implements Runnable
 {
-  HMMERCommands cmds = new HMMERCommands();
-  AlignFrame af;
-
-  AlignViewport viewport;
-  AlignmentI alignment;
-  SequenceGroup group;
-
-  List<ArgumentI> params;
+  static final String ARG_AMINO = "--amino";
 
+  static final String ARG_DNA = "--dna";
 
-  boolean forGroup = false;
+  static final String ARG_RNA = "--rna";
 
-  File hmmTemp = null;
-
-  File stoTemp = null;
+  AlignmentI alignment;
 
-  long barID;
-  
   /**
-   * This is used for validation purposes. Do not use!
+   * Constructor
    * 
-   * @param viewport
+   * @param alignFrame
+   * @param args
    */
-  public HMMBuildThread(AlignmentI alignment)
-  {
-    this.alignment = alignment;
-    forGroup = false;
-  }
-
-  public HMMBuildThread(AlignFrame af, List<ArgumentI> args)
+  public HMMBuildThread(AlignFrame alignFrame, List<ArgumentI> args)
   {
-    this.af = af;
-    if (af.getViewport().getSelectionGroup() != null)
-    {
-      group = af.getViewport().getSelectionGroup();
-      forGroup = true;
-    }
-    viewport = af.getViewport();
-    alignment = viewport.getAlignment();
-    params = args;
-
+    super(alignFrame, args);
   }
 
   /**
-   * Builds a HMM from an alignment, then imports and adds it to the alignment.
+   * Builds a HMM from an alignment (and/or groups), then imports and adds it to
+   * the alignment (and/or groups)
    */
   @Override
   public void run()
   {
-    barID = System.currentTimeMillis();
+    long msgID = System.currentTimeMillis();
     if (af != null)
     {
-    af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
-            barID);
-    }
-    cmds.HMMERFOLDER = Cache.getProperty(Preferences.HMMER_PATH);
-    if (alignment == null && group == null)
-    {
-      JOptionPane.showMessageDialog(af,
-              MessageManager.getString("warn.no_sequence_data"));
-      return;
+      af.setProgressBar(MessageManager.getString("status.running_hmmbuild"),
+              msgID);
     }
+
+    AlignViewportI viewport = af.getViewport();
     try
     {
-      hmmTemp = File.createTempFile("hmm", ".hmm");
-      hmmTemp.deleteOnExit();
-      stoTemp = File.createTempFile("output", ".sto");
-      stoTemp.deleteOnExit();
-    } catch (IOException e1)
+      if (viewport != null)
+      {
+        alignment = viewport.getAlignment();
+      }
+      List<SequenceGroup> groups = new ArrayList<>();
+      if (params != null)
+      {
+        for (ArgumentI arg : params)
+        {
+          String name = arg.getName();
+          if (MessageManager.getString("label.hmmbuild_for").equals(name))
+          {
+            String value = arg.getValue();
+            if (MessageManager.getString("label.alignment").equals(value))
+            {
+              alignment = viewport.getAlignment();
+            }
+            else if (MessageManager.getString("label.groups_and_alignment")
+                    .equals(value))
+            {
+              alignment = viewport.getAlignment();
+              groups.addAll(viewport.getAlignment().getGroups());
+            }
+            else if (MessageManager.getString("label.groups").equals(value))
+            {
+              alignment = null;
+              groups = viewport.getAlignment().getGroups();
+            }
+            else if ("label.selected_group".equals(value))
+            {
+              alignment = null;
+              groups.add(viewport.getSelectionGroup());
+            }
+          }
+          else if (MessageManager.getString("label.use_reference")
+                  .equals(name))
+          {
+            // todo disable this option if no RF annotation on alignment
+            if (!af.getViewport().hasReferenceAnnotation())
+            {
+              JvOptionPane.showInternalMessageDialog(af, MessageManager
+                      .getString("warn.no_reference_annotation"));
+              // return;
+            }
+          }
+        }
+      }
+
+      if (alignment != null)
+      {
+        runHMMBuild(null);
+      }
+
+      if (alignment == null)
+      {
+        alignment = viewport.getAlignment();
+      }
+
+      for (SequenceGroup grp : groups)
+      {
+        runHMMBuild(grp);
+      }
+    } finally
     {
-      e1.printStackTrace();
+      if (af != null)
+      {
+        af.setProgressBar("", msgID);
+      }
     }
+  }
 
-    try
+  /**
+   * Runs hmmbuild on the alignment, or on the group if one is specified
+   * 
+   * @param grp
+   */
+  private void runHMMBuild(SequenceGroup group)
+  {
+    if (alignment == null && group == null)
     {
+      JOptionPane.showMessageDialog(af,
+              MessageManager.getString("warn.no_sequence_data"));
+    }
+    File hmmFile = null;
+    File alignmentFile = null;
     try
     {
-        SequenceI[] array;
-        List<SequenceI> seqs = alignment
-                .getHMMConsensusSequences(true);
-        cmds.setHmmSeqs(seqs);
-        if (forGroup)
+      hmmFile = createTempFile("hmm", ".hmm");
+      alignmentFile = createTempFile("output", ".sto");
+      SequenceI[] array;
+      List<SequenceI> hmmSeqs = null;
+      if (group != null)
+      {
+        hmmSeqs = group.getHMMConsensusSequences();
+        array = group.getSelectionAsNewSequences(alignment);
+      }
+      else
+      {
+        hmmSeqs = alignment.getHMMConsensusSequences();
+        // todo pad gaps in an unaligned SequenceGroup as well?
+        if (!alignment.isAligned())
         {
-          array = group.getSelectionAsNewSequences(alignment);
+          alignment.padGaps();
         }
-        else
+        array = alignment.getSequencesArray();
+      }
+
+      if (array.length < 1)
+      {
+        if (af != null)
         {
-          if (!alignment.isAligned())
-          {
-            alignment.padGaps();
-          }
-          array = alignment.getSequencesArray();
+          JOptionPane.showMessageDialog(af,
+                  MessageManager.getString("warn.no_sequence_data"));
         }
-        if (array.length < 1)
+        return;
+      }
+
+      /*
+       * copy over sequences excluding hmm consensus sequences
+       */
+      SequenceI[] newArr = new SequenceI[array.length - hmmSeqs.size()];
+      int index = 0;
+      for (SequenceI seq : array)
+      {
+        if (seq.isHMMConsensusSequence())
         {
-          if (af != null)
-          {
-            JOptionPane.showMessageDialog(af,
-                  MessageManager.getString("warn.no_sequence_data"));
-          }
-          return;
+          alignment.deleteSequence(seq);
         }
-        SequenceI[] newArr = new SequenceI[array.length];
-        int index = 0;
-        for (SequenceI seq : array)
+        else
         {
           newArr[index] = new Sequence(seq);
           index++;
         }
+      }
 
-        cmds.uniquifySequences(newArr);
-        cmds.exportData(newArr, stoTemp, null, null);
-        jalview.analysis.SeqsetUtils.deuniquify(cmds.hash, array);
+      stashSequences(newArr);
 
-    } catch (FileNotFoundException e)
-    {
-      // TODO Auto-generated catch block
-      e.printStackTrace();
+      exportStockholm(newArr, alignmentFile,
+              group != null ? group : alignment);
 
-    }
-    try
-    {
-        boolean ran = runCommand();
-        if (!ran)
-        {
-          return;
-        }
-    } catch (IOException | InterruptedException e)
-    {
-      // TODO Auto-generated catch block
-      e.printStackTrace();
-    }
-    try
-    {
+      recoverSequences(array);
 
-      importData();
-    } catch (IOException | InterruptedException e)
-    {
-      // TODO Auto-generated catch block
-      e.printStackTrace();
-    }
+      boolean ran = runCommand(alignmentFile, hmmFile, group);
+      if (!ran)
+      {
+        return;
+      }
+      importData(hmmFile, group);
     } catch (Exception e)
     {
       e.printStackTrace();
     } finally
     {
-      if (af != null)
+      if (hmmFile != null)
+      {
+        hmmFile.delete();
+      }
+      if (alignmentFile != null)
       {
-        af.setProgressBar(
-                MessageManager.getString("status.running_hmmbuild"),
-              barID);
+        alignmentFile.delete();
       }
     }
   }
 
-  
-
   /**
-   * Executes the hmmbuild command in the command line.
+   * Constructs and executes the hmmbuild command as a separate process
+   * 
+   * @param sequences
+   *          the alignment from which the HMM is built
+   * @param hmm
+   *          the output file to which the HMM is written
+   * @param group
+   *          (optional) group for which the hmm is generated
    * 
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
-  private boolean runCommand() throws IOException, InterruptedException
+  private boolean runCommand(File sequences, File hmm, SequenceGroup group)
+          throws IOException, InterruptedException
   {
-    File file = new File(cmds.HMMERFOLDER + "/hmmbuild");
-    if (!file.canExecute())
+
+    String cmd = getCommandPath(HMMBUILD);
+    if (cmd == null)
     {
-      file = new File(cmds.HMMERFOLDER + "/hmmbuild.exe");
-      {
-        if (!file.canExecute())
-        {
-          if (af != null)
-          {
-            JvOptionPane.showInternalMessageDialog(af,
-                    MessageManager.getString("warn.hmmbuild_failed"));
-          }
-          return false;
-        }
-      }
+      return false;
     }
-    String command = cmds.HMMERFOLDER + cmds.HMMBUILD + cmds.SPACE;
+    List<String> args = new ArrayList<>();
+    args.add(cmd);
     String name = null;
 
     if (params != null)
@@ -221,83 +259,102 @@ public class HMMBuildThread implements Runnable
         {
         case "HMM Name":
           name = arg.getValue();
+          name = name.trim();
           break;
         case "Use Reference Annotation":
-          command += "--hand ";
-          if (!af.getViewport().hasReferenceAnnotation())
-          {
-            JvOptionPane.showInternalMessageDialog(af, MessageManager
-                    .getString("warn.no_reference_annotation"));
-            return false;
-          }
+          args.add("--hand");
           break;
-
         }
-
       }
     }
 
-    if (name == null || name == "" || name == " ")
+    if (group != null)
     {
-      if (forGroup)
-      {
-        name = group.getName();
-      }
-      else
+      name = group.getName() + "_HMM";
+    }
+
+    if (name == null || "".equals(name))
+    {
+      if (af != null)
       {
-        if (af != null)
+        if (af.getTitle().length() < 15)
         {
           name = af.getTitle();
         }
-        if (name == null || name == "" || name == " " || name == "  ")
-        {
-          name = "Alignment";
-        }
       }
+      if (name == null || "".equals(name))
+      {
+        name = "Alignment";
+      }
+
     }
 
-    command += "-n " + name + cmds.SPACE;
+    args.add("-n");
+    args.add(name.replace(' ', '_'));
     if (!alignment.isNucleotide())
     {
-      command += cmds.FORCEAMINO; // TODO check for rna
+      args.add(ARG_AMINO); // TODO check for rna
     }
     else
     {
-      command += cmds.FORCEDNA;
+      args.add(ARG_DNA);
     }
 
-    command += hmmTemp.getAbsolutePath()
-            + cmds.SPACE + stoTemp.getAbsolutePath() + cmds.SPACE;
-    return cmds.runCommand(command);
+    args.add(hmm.getAbsolutePath());
+    args.add(sequences.getAbsolutePath());
+
+    return runCommand(args);
   }
-  
+
   /**
-   * Imports the .hmm file produced by hmmbuild.
+   * Imports the .hmm file produced by hmmbuild, and inserts the HMM consensus
+   * sequence (with attached HMM profile) as the first sequence in the alignment
+   * or group for which it was generated
    * 
+   * @param hmmFile
+   * @oparam group (optional) the group for which the hmm was generated
    * @throws IOException
    * @throws InterruptedException
    */
-  private void importData() throws IOException, InterruptedException
+  private void importData(File hmmFile, SequenceGroup group)
+          throws IOException, InterruptedException
   {
-    if (af != null)
+    HMMFile file = new HMMFile(
+            new FileParse(hmmFile.getAbsolutePath(), DataSourceType.FILE));
+    SequenceI[] seqs = file.getSeqsAsArray();
+    SequenceI seq = seqs[0];
+    seq.createDatasetSequence();
+    if (group != null)
     {
-      cmds.addHMMConsensusSequences(af);
-
-      FileLoader loader = new FileLoader();
-      loader.LoadFileOntoAlignmentWaitTillLoaded(viewport,
-              hmmTemp.getAbsolutePath(), DataSourceType.FILE,
-              FileFormat.HMMER3);
+      seq.insertCharAt(0, group.getStartRes(), '-');
+      seq.insertCharAt(group.getEndRes() + 1,
+              alignment.getWidth() - group.getEndRes() - 1, '-');
+      seq.updateHMMMapping();
+      SequenceI topSeq = group.getSequencesInOrder(alignment)[0];
+      int topIndex = alignment.findIndex(topSeq);
+      alignment.insertSequenceAt(topIndex, seq);
+      group.setSeqrep(seq);
+      group.addSequence(seq, false);
     }
     else
     {
-      HMMFile file = new HMMFile(new FileParse(hmmTemp.getAbsolutePath(),
-              DataSourceType.FILE));
-      alignment.addSequence(file.getSeqsAsArray()[0]);
+      alignment.insertSequenceAt(0, seq);
+    }
+
+    AlignViewport viewport = af.getViewport();
+    if (viewport != null)
+    {
+      viewport.alignmentChanged(viewport.getAlignPanel());
+      viewport.getAlignPanel().adjustAnnotationHeight();
+      viewport.updateSequenceIdColours();
+
+      if (viewport.getAlignPanel().alignFrame.getSelectedHMM() == null)
+      {
+        viewport.getAlignPanel().alignFrame.setSelectedHMMSequence(seq);
+      }
     }
-    hmmTemp.delete();
-    stoTemp.delete();
   }
-  
+
   /**
    * Runs hmmbuild, and waits for the results to be imported before continuing
    */