JAL-2937 Cygwin path preference, method refactoring
authorMungo Carstairs <gmungoc@gmail.com>
Mon, 9 Apr 2018 16:09:32 +0000 (17:09 +0100)
committerMungo Carstairs <gmungoc@gmail.com>
Mon, 9 Apr 2018 16:09:32 +0000 (17:09 +0100)
resources/lang/Messages.properties
src/jalview/gui/Preferences.java
src/jalview/hmmer/HMMAlign.java
src/jalview/hmmer/HMMSearch.java
src/jalview/hmmer/HmmerCommand.java
src/jalview/jbgui/GPreferences.java
src/jalview/util/Platform.java

index 06c8b6b..bcd0bbd 100644 (file)
@@ -1379,7 +1379,8 @@ label.hmmbuild_group = Build HMM from Selected Group
 label.group_hmmbuild = Build HMM from Group
 label.hmmsearch = hmmsearch
 label.hmmer_location = HMMER Binaries Installation Location
-warn.null_hmm = Please ensure the alignment contains a hidden Markov model.
+label.cygwin_location = Cygwin Binaries Installation Location (Windows)
+warn.null_hmm = Please ensure the alignment contains a hidden Markov model
 label.ignore_below_background_frequency = Ignore Below Background Frequency
 label.information_description = Information content, measured in bits
 warn.no_selected_hmm = Please select a hidden Markov model sequence.
@@ -1390,14 +1391,12 @@ label.no_sequences_found = No matching sequences, or an error occurred.
 label.hmmer = HMMER
 label.trim_termini = Trim Non-Matching Termini
 label.trim_termini_desc = If true, non-matching regions on either end of the resulting alignment are removed.
-label.no_of_sequences = Sequences Returned
-label.freq_alignment = Use Alignment Background Frequencies
-label.freq_uniprot = Use Uniprot Background Frequencies
-label.hmmalign_label = hmmalign Options
-label.hmmsearch_label = hmmsearch Options
-label.hmmbuild_not_found = The hmmbuild binary was not found
-label.hmmalign_not_found = The hmmalign binary was not found
-label.hmmsearch_not_found = The hmmsearch binary was not found
+label.no_of_sequences = Number of sequences returned
+label.freq_alignment = Use alignment background frequencies
+label.freq_uniprot = Use Uniprot background frequencies
+label.hmmalign_label = hmmalign options
+label.hmmsearch_label = hmmsearch options
+label.executable_not_found = The ''{0}'' executable file was not found
 warn.hmm_command_failed = hmm command not found
 label.invalid_folder = Invalid Folder
 label.folder_not_exists = HMMER binaries not found. \n Please enter the path to the HMMER binaries (if installed).
index df35b5e..9f9d6f8 100755 (executable)
@@ -37,6 +37,7 @@ import jalview.urls.UrlLinkTableModel;
 import jalview.urls.api.UrlProviderFactoryI;
 import jalview.urls.api.UrlProviderI;
 import jalview.urls.desktop.DesktopUrlProviderFactory;
+import jalview.util.FileUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.UrlConstants;
@@ -61,6 +62,7 @@ import javax.swing.JColorChooser;
 import javax.swing.JFileChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
+import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
 import javax.swing.RowFilter;
 import javax.swing.RowSorter;
@@ -108,6 +110,8 @@ public class Preferences extends GPreferences
   
   public static final String HMMER_PATH = "HMMER_PATH";
 
+  public static final String CYGWIN_PATH = "CYGWIN_PATH";
+
   public static final String HMMSEARCH_DB_PATHS = "HMMSEARCH_DB_PATHS";
 
   public static final String HMMSEARCH_DBS = "HMMSEARCH_DBS";
@@ -235,7 +239,7 @@ public class Preferences extends GPreferences
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        validateHMMERPath(true);
+        validateHmmerPath();
       }
     });
     hmmerPath.addFocusListener(new FocusAdapter()
@@ -243,7 +247,24 @@ public class Preferences extends GPreferences
       @Override
       public void focusLost(FocusEvent e)
       {
-        validateHMMERPath(true);
+        validateHmmerPath();
+      }
+    });
+    cygwinPath.setText(Cache.getProperty(CYGWIN_PATH));
+    cygwinPath.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        validateCygwinPath();
+      }
+    });
+    cygwinPath.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        validateCygwinPath();
       }
     });
 
@@ -712,8 +733,8 @@ public class Preferences extends GPreferences
             Boolean.toString(hmmerBackgroundUniprot.isSelected()));
     Cache.applicationProperties.setProperty("SEQUENCES_TO_KEEP",
             hmmerSequenceCount.getText());
-    Cache.applicationProperties.setProperty(HMMER_PATH,
-            hmmerPath.getText());
+    Cache.setOrRemove(HMMER_PATH, hmmerPath.getText());
+    Cache.setOrRemove(CYGWIN_PATH, cygwinPath.getText());
     AlignFrame[] frames = Desktop.getAlignFrames();
     if (frames != null && frames.length > 0)
     {
@@ -1264,23 +1285,27 @@ public class Preferences extends GPreferences
   }
   
   /**
-   * Returns true if hmmer path is to a folder that contains an executable
-   * hmmbuild or hmmbuild.exe, else false (optionally after showing a warning
-   * dialog)
+   * Returns true if the given text field contains a path to a folder that
+   * contains an executable with the given name, else false (after showing a
+   * warning dialog). The executable name will be tried with .exe appended if not
+   * found.
+   * 
+   * @param textField
+   * @param executable
    */
-  @Override
-  protected boolean validateHMMERPath(boolean showWarning)
+  protected boolean validateExecutablePath(JTextField textField, String executable)
   {
-    String folder = hmmerPath.getText().trim();
+    String folder = textField.getText().trim();
 
-    if (HmmerCommand.getExecutable(HmmerCommand.HMMBUILD, folder) != null)
+    if (FileUtils.getExecutable(executable, folder) != null)
     {
       return true;
     }
-    if (showWarning && folder.length() > 0)
+    if (folder.length() > 0)
     {
       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-              MessageManager.getString("label.hmmbuild_not_found"),
+              MessageManager.formatMessage("label.executable_not_found",
+                      executable),
               MessageManager.getString("label.invalid_folder"),
               JvOptionPane.ERROR_MESSAGE);
     }
@@ -1357,6 +1382,18 @@ public class Preferences extends GPreferences
     }
   }
 
+  @Override
+  protected void validateHmmerPath()
+  {
+    validateExecutablePath(hmmerPath, HmmerCommand.HMMBUILD);
+  }
+
+  @Override
+  protected void validateCygwinPath()
+  {
+    validateExecutablePath(cygwinPath, "run");
+  }
+
   public class OptionsParam
   {
     private String name;
index 44828aa..befdc55 100644 (file)
@@ -14,6 +14,7 @@ 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;
@@ -86,9 +87,9 @@ public class HMMAlign extends HmmerCommand
       Hashtable sequencesHash = stashSequences(seqs);
       try
       {
-        File modelFile = createTempFile("hmm", ".hmm");
-        File alignmentFile = createTempFile("output", ".sto");
-        File resultFile = createTempFile("input", ".sto");
+        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());
index 30299e9..6f73f2f 100644 (file)
@@ -11,6 +11,7 @@ import jalview.gui.JvOptionPane;
 import jalview.io.DataSourceType;
 import jalview.io.FileParse;
 import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
 import jalview.util.MessageManager;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.simple.BooleanOption;
@@ -71,9 +72,11 @@ public class HMMSearch extends HmmerCommand
 
     try
     {
-      File hmmFile = createTempFile("hmm", ".hmm");
-      File hitsAlignmentFile = createTempFile("hitAlignment", ".sto");
-      File searchOutputFile = createTempFile("searchOutput", ".sto");
+      File hmmFile = FileUtils.createTempFile("hmm", ".hmm");
+      File hitsAlignmentFile = FileUtils.createTempFile("hitAlignment",
+              ".sto");
+      File searchOutputFile = FileUtils.createTempFile("searchOutput",
+              ".sto");
 
       exportHmm(hmm, hmmFile.getAbsoluteFile());
 
@@ -196,7 +199,7 @@ public class HMMSearch extends HmmerCommand
        * no external database specified for search, so
        * export current alignment as 'database' to search
        */
-      databaseFile = createTempFile("database", ".sto");
+      databaseFile = FileUtils.createTempFile("database", ".sto");
       AlignmentI al = af.getViewport().getAlignment();
       AlignmentI copy = new Alignment(al);
       SequenceI hmms = copy.getHmmConsensus();
index fe6c0f9..9bec181 100644 (file)
@@ -14,7 +14,9 @@ import jalview.gui.JvOptionPane;
 import jalview.gui.Preferences;
 import jalview.io.HMMFile;
 import jalview.io.StockholmFile;
+import jalview.util.FileUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.params.ArgumentI;
 
 import java.io.BufferedReader;
@@ -22,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 
@@ -63,7 +66,8 @@ public abstract class HmmerCommand implements Runnable
    */
   public static boolean isHmmerAvailable()
   {
-    File exec = getExecutable(HMMBUILD, Cache.getProperty(Preferences.HMMER_PATH));
+    File exec = FileUtils.getExecutable(HMMBUILD,
+            Cache.getProperty(Preferences.HMMER_PATH));
     return exec != null;
   }
 
@@ -100,9 +104,12 @@ public abstract class HmmerCommand implements Runnable
   public boolean runCommand(List<String> command)
           throws IOException
   {
+    List<String> commands = Platform.isWindows() ? wrapWithCygwin(command)
+            : command;
+
     try
     {
-      ProcessBuilder pb = new ProcessBuilder(command);
+      ProcessBuilder pb = new ProcessBuilder(commands);
       pb.redirectErrorStream(true); // merge syserr to sysout
       final Process p = pb.start();
       new Thread(new Runnable()
@@ -128,7 +135,13 @@ public abstract class HmmerCommand implements Runnable
       }).start();
 
       p.waitFor();
-      return p.exitValue() == 0; // 0 is success, by convention
+      int exitValue = p.exitValue();
+      if (exitValue != 0)
+      {
+        Cache.log.error("Command failed, return code = " + exitValue);
+        Cache.log.error("Command/args were: " + commands.toString());
+      }
+      return exitValue == 0; // 0 is success, by convention
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -137,6 +150,40 @@ public abstract class HmmerCommand implements Runnable
   }
 
   /**
+   * Converts the given command to a Cygwin "run" command wrapper
+   * 
+   * @param command
+   * @return
+   */
+  protected List<String> wrapWithCygwin(List<String> command)
+  {
+    File runCygwin = FileUtils.getExecutable("run",
+            Cache.getProperty(Preferences.CYGWIN_PATH));
+    if (runCygwin == null)
+    {
+      Cache.log.error("Cygwin shell not found");
+      return command;
+    }
+
+    List<String> wrapped = new ArrayList<>();
+    wrapped.add(runCygwin.getAbsolutePath());
+    if (!command.isEmpty())
+    {
+      wrapped.add(command.get(0));
+      // wrapped.add("--quote");
+      StringBuilder args = new StringBuilder();
+      for (String arg : command.subList(1, command.size()))
+      {
+        args.append(" ").append(arg);
+      }
+      wrapped.add(args.toString());
+    }
+    // TODO this doesn't yet pass parameters successfully
+
+    return wrapped;
+  }
+
+  /**
    * Exports an alignment, and reference (RF) annotation if present, to the
    * specified file, in Stockholm format
    * 
@@ -204,7 +251,7 @@ public abstract class HmmerCommand implements Runnable
   protected String getCommandPath(String cmd)
   {
     String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
-    File file = getExecutable(cmd, binariesFolder);
+    File file = FileUtils.getExecutable(cmd, binariesFolder);
     if (file == null && af != null)
     {
         JvOptionPane.showInternalMessageDialog(af,
@@ -215,51 +262,6 @@ public abstract class HmmerCommand implements Runnable
   }
 
   /**
-   * Answers the executable file for the given hmmer command, or null if not
-   * found or not executable. The path to the executable is the command name
-   * prefixed by the hmmer binaries folder path, optionally with .exe appended.
-   * 
-   * @param cmd
-   *          hmmer command short name, for example hmmbuild
-   * @param binaryPath
-   *          parent folder containing hmmer executables
-   * @return
-   */
-  public static File getExecutable(String cmd, String binaryPath)
-  {
-    File file = new File(binaryPath, cmd);
-    if (!file.canExecute())
-    {
-      file = new File(binaryPath, cmd + ".exe");
-      {
-        if (!file.canExecute())
-        {
-          file = null;
-        }
-      }
-    }
-    return file;
-  }
-
-  /**
-   * A convenience method to create a temporary file that is deleted on exit of
-   * the JVM
-   * 
-   * @param prefix
-   * @param suffix
-   * @return
-   * @throws IOException
-   */
-  protected File createTempFile(String prefix, String suffix)
-          throws IOException
-  {
-    File f = File.createTempFile(prefix, suffix);
-    f.deleteOnExit();
-    return f;
-
-  }
-
-  /**
    * Exports an HMM to the specified file
    * 
    * @param hmm
index 358298d..6ff335e 100755 (executable)
@@ -26,6 +26,7 @@ import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -284,6 +285,8 @@ public class GPreferences extends JPanel
 
   protected JTextField hmmerPath = new JTextField();
 
+  protected JTextField cygwinPath = new JTextField();
+
   /*
    * DAS Settings tab
    */
@@ -424,12 +427,19 @@ public class GPreferences extends JPanel
   private JPanel initHMMERTab()
   {
     hmmerTab.setLayout(null);
+    final int lineSpacing = 20;
 
-    JLabel installationLocation = new JLabel(
+    /*
+     * path to hmmer binaries folder
+     */
+    JLabel hmmerLocation = new JLabel(
             MessageManager.getString("label.hmmer_location"));
-    installationLocation.setFont(LABEL_FONT);
-    installationLocation.setBounds(new Rectangle(22, 10, 250, 23));
-    hmmerPath.setBounds(new Rectangle(22, 30, 300, 23));
+    hmmerLocation.setFont(LABEL_FONT);
+    int xPos = 22;
+    int yPos = 10;
+    hmmerLocation.setBounds(new Rectangle(xPos, yPos, 250, 23));
+    yPos += lineSpacing;
+    hmmerPath.setBounds(new Rectangle(xPos, yPos, 300, 23));
     hmmerPath.addMouseListener(new MouseAdapter()
     {
       @Override
@@ -441,54 +451,96 @@ public class GPreferences extends JPanel
           if (chosen != null)
           {
             hmmerPath.setText(chosen);
+            validateHmmerPath();
           }
         }
       }
     });
+    hmmerTab.add(hmmerLocation);
+    hmmerTab.add(hmmerPath);
 
-    JLabel hmmalign = new JLabel(
-            MessageManager.getString("label.hmmalign_label"));
-    hmmalign.setFont(LABEL_FONT);
-    hmmalign.setBounds(new Rectangle(22, 50, 200, 23));
+    /*
+     * path to Cygwin binaries folder (for Windows)
+     */
+    if (Platform.isWindows())
+    {
+      JLabel cygwinLocation = new JLabel(
+              MessageManager.getString("label.cygwin_location"));
+      cygwinLocation.setFont(LABEL_FONT);
+      yPos += lineSpacing * 2;
+      cygwinLocation.setBounds(new Rectangle(xPos, yPos, 250, 23));
+      yPos += lineSpacing;
+      cygwinPath.setBounds(new Rectangle(xPos, yPos, 300, 23));
+      cygwinPath.addMouseListener(new MouseAdapter()
+      {
+        @Override
+        public void mouseClicked(MouseEvent e)
+        {
+          if (e.getClickCount() == 2)
+          {
+            String chosen = openFileChooser(true);
+            if (chosen != null)
+            {
+              cygwinPath.setText(chosen);
+              validateCygwinPath();
+            }
+          }
+        }
+      });
+      hmmerTab.add(cygwinLocation);
+      hmmerTab.add(cygwinPath);
+    }
 
+    /*
+     * preferences for hmmalign
+     */
+    yPos += lineSpacing * 2;
+    JPanel alignOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
+    JvSwingUtils.createTitledBorder(alignOptions,
+            MessageManager.getString("label.hmmalign_label"), true);
+    yPos += lineSpacing;
     hmmrTrimTermini.setFont(LABEL_FONT);
     hmmrTrimTermini.setText(MessageManager.getString("label.trim_termini"));
-    hmmrTrimTermini.setBounds(new Rectangle(22, 70, 200, 23));
-
-    JLabel hmmsearch = new JLabel(
-            MessageManager.getString("label.hmmsearch_label"));
-    hmmsearch.setFont(LABEL_FONT);
-    hmmsearch.setBounds(new Rectangle(22, 90, 200, 23));
+    alignOptions.add(hmmrTrimTermini);
+    hmmerTab.add(alignOptions);
+    alignOptions.setBounds(new Rectangle(xPos, yPos, 300, 43));
 
+    /*
+     * preferences for hmmsearch
+     */
+    yPos += lineSpacing * 3;
+    JPanel searchOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
+    JvSwingUtils.createTitledBorder(searchOptions,
+            MessageManager.getString("label.hmmsearch_label"), true);
+    searchOptions.setBounds(new Rectangle(xPos, yPos, 300, 43));
     JLabel sequencesToKeep = new JLabel(
             MessageManager.getString("label.no_of_sequences"));
     sequencesToKeep.setFont(LABEL_FONT);
-    sequencesToKeep.setBounds(new Rectangle(22, 110, 125, 23));
-    hmmerSequenceCount.setBounds(new Rectangle(150, 110, 40, 23));
+    // hmmerSequenceCount.setBounds(new Rectangle(xPos + 250, yPos, 60, 23));
+    searchOptions.add(sequencesToKeep);
+    searchOptions.add(hmmerSequenceCount);
+    hmmerTab.add(searchOptions);
 
     ButtonGroup backgroundFreqSource = new ButtonGroup();
     backgroundFreqSource.add(hmmerBackgroundUniprot);
     backgroundFreqSource.add(hmmerBackgroundAlignment);
     backgroundFreqSource.setSelected(hmmerBackgroundUniprot.getModel(), true);
 
+    /*
+     * preferences for Information Content annotation
+     */
+    yPos += lineSpacing * 3;
     hmmerBackgroundUniprot.setText(MessageManager.getString("label.freq_uniprot"));
     hmmerBackgroundUniprot.setFont(LABEL_FONT);
-    hmmerBackgroundUniprot.setBounds(new Rectangle(22, 130, 255, 23));
+    hmmerBackgroundUniprot.setBounds(new Rectangle(xPos, yPos, 255, 23));
 
+    yPos += lineSpacing;
     hmmerBackgroundAlignment.setText(MessageManager.getString("label.freq_alignment"));
     hmmerBackgroundAlignment.setFont(LABEL_FONT);
-    hmmerBackgroundAlignment.setBounds(new Rectangle(22, 150, 300, 23));
+    hmmerBackgroundAlignment.setBounds(new Rectangle(xPos, yPos, 300, 23));
 
     hmmerTab.add(hmmerBackgroundUniprot);
     hmmerTab.add(hmmerBackgroundAlignment);
-    hmmerTab.add(hmmalign);
-    hmmerTab.add(hmmsearch);
-    hmmerTab.add(installationLocation);
-    hmmerTab.add(hmmerPath);
-    hmmerTab.add(hmmrTrimTermini);
-    hmmerTab.add(sequencesToKeep);
-    hmmerTab.add(sequencesToKeep);
-    hmmerTab.add(hmmerSequenceCount);
 
     return hmmerTab;
   }
@@ -1406,11 +1458,6 @@ public class GPreferences extends JPanel
     return false;
   }
 
-  protected boolean validateHMMERPath(boolean showWarning)
-  {
-    return false;
-  }
-
   /**
    * Initialises the Visual tabbed panel.
    * 
@@ -1915,8 +1962,11 @@ public class GPreferences extends JPanel
 
   }
 
-  public void hmmerPath_actionPerformed(ActionEvent e)
+  protected void validateHmmerPath()
   {
+  }
 
+  protected void validateCygwinPath()
+  {
   }
 }
index 2c74609..7c0e15d 100644 (file)
@@ -51,7 +51,7 @@ public class Platform
   }
 
   /**
-   * Check if we are on a Microsoft plaform...
+   * Check if we are on a Microsoft platform...
    * 
    * @return true if we have to cope with another platform variation
    */