Merge branch 'patch/JAL-4195_cli_eps_export_npe' into develop
authorJames Procter <j.procter@dundee.ac.uk>
Wed, 7 Jun 2023 16:16:01 +0000 (17:16 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Wed, 7 Jun 2023 16:16:01 +0000 (17:16 +0100)
12 files changed:
help/help/html/features/clarguments-reference.html
src/jalview/bin/Commands.java
src/jalview/bin/Jalview.java
src/jalview/bin/argparser/Arg.java
src/jalview/bin/argparser/ArgParser.java
src/jalview/gui/Desktop.java
src/jalview/gui/ImageExporter.java
src/jalview/io/HTMLOutput.java
src/jalview/io/exceptions/ImageOutputException.java [new file with mode: 0644]
src/jalview/workers/AlignCalcManager.java
test/jalview/bin/CommandsTest.java
test/jalview/bin/CommandsTest2.java

index 0221a67..dd0bd52 100644 (file)
     </tr>
 
     <tr valign="top">
-    <td><code>&#8209;&#8209;title&nbsp;<em>"string""</em></code></td>
+    <td><code>&#8209;&#8209;title&nbsp;<em>"string"</em></code></td>
     <td>Specifies the title for the open alignment window as <em>string</em>.</td>
     <td></td>
     <td align="center">&#x2713;</td>
 
     <tr valign="top">
     <td><code>&#8209;&#8209;colour&nbsp;<em>name</em></code></td>
-    <td>Applies the colour scheme <em>name</em> to the open alignment window.  Valid values for <em>name</em> are:
+    <td>Applies the colour scheme <em>name</em> to the open alignment window.  Valid values for <em>name</em> include:
     <br/>
     <code>clustal</code>,
     <br/>
     <code>t-coffee-scores</code>,
     <br/>
     <code>sequence-id</code>.
+    <br/>
+    <br/>
+    Names of user defined schemes will also work, and jalview colour scheme specifications like:
+    <br/>
+       <code>&#8209;&#8209;colour "D,E=red; K,R,H=0022FF; C,c=yellow"</code>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
index 30fdc30..b9c04f5 100644 (file)
@@ -44,6 +44,9 @@ import jalview.io.FileLoader;
 import jalview.io.HtmlSvgOutput;
 import jalview.io.IdentifyFile;
 import jalview.io.NewickFile;
+import jalview.io.exceptions.ImageOutputException;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
 import jalview.structure.StructureImportSettings.TFType;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.FileUtils;
@@ -100,6 +103,16 @@ public class Commands
         theseArgsWereParsed &= processLinked(id);
         processGroovyScript(id);
         boolean processLinkedOkay = theseArgsWereParsed;
+        
+        // wait around until alignFrame isn't busy
+        AlignFrame af=afMap.get(id);
+        while (af!=null && af.getViewport().isCalcInProgress())
+        {
+          try {
+            Thread.sleep(25);
+          } catch (Exception q) {};
+        }
+        
         theseArgsWereParsed &= processImages(id);
         if (processLinkedOkay)
           theseArgsWereParsed &= processOutput(id);
@@ -107,7 +120,7 @@ public class Commands
         // close ap
         if (avm.getBoolean(Arg.CLOSE))
         {
-          AlignFrame af = afMap.get(id);
+          af = afMap.get(id);
           if (af != null)
           {
             af.closeMenuItem_actionPerformed(true);
@@ -249,7 +262,15 @@ public class Commands
                   Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", "");
           if ("" != colour)
           {
-            af.changeColour_actionPerformed(colour);
+            ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+                    af.getViewport(), af.getViewport().getAlignment(), colour);
+            
+            if (cs==null && !"None".equals(colour))
+            {
+              Console.warn("Couldn't parse '"+colour+"' as a colourscheme.");
+            } else {
+              af.changeColour(cs);
+            }
             Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
           }
 
@@ -717,7 +738,7 @@ public class Commands
         Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
 
         Console.info("Writing " + file);
-
+        try {
         switch (type)
         {
 
@@ -738,6 +759,7 @@ public class Commands
           break;
 
         case "biojs":
+          Console.debug("Creating BioJS MSA Viwer HTML file: " + fileName);
           try
           {
             BioJsHTMLOutput.refreshVersionInfo(
@@ -748,17 +770,16 @@ public class Commands
           }
           BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
           bjs.exportHTML(fileName);
-          Console.debug("Creating BioJS MSA Viwer HTML file: " + fileName);
           break;
 
         case "eps":
-          af.createEPS(file, name);
           Console.debug("Creating EPS file: " + fileName);
+          af.createEPS(file, name);
           break;
 
         case "imagemap":
-          af.createImageMap(file, name);
           Console.debug("Creating ImageMap file: " + fileName);
+          af.createImageMap(file, name);
           break;
 
         default:
@@ -766,6 +787,9 @@ public class Commands
                   + "' not known. Ignoring");
           break;
         }
+        } catch (Exception ioex) {
+          Console.warn("Unexpected error during export",ioex);
+        }
       }
     }
     return true;
index b29d9d7..e4f9ec6 100755 (executable)
@@ -1005,9 +1005,7 @@ public class Jalview
             ex.printStackTrace(System.err);
           }
         }
-        // TODO - load PDB structure(s) to alignment JAL-629
-        // (associate with identical sequence in alignment, or a specified
-        // sequence)
+        
         if (groovyscript != null)
         {
           // Execute the groovy script after we've done all the rendering stuff
@@ -1800,8 +1798,14 @@ public class Jalview
     }
   }
 
-  /*
-   * testoutput for string values
+  /******************************
+   * 
+   * TEST OUTPUT METHODS
+   * 
+   ******************************/
+  /**
+   * method for reporting string values parsed/processed during tests
+   * 
    */
   protected static void testoutput(ArgParser ap, Arg a, String s1,
           String s2)
@@ -1825,6 +1829,10 @@ public class Jalview
     testoutput(true, a, s1, s2);
   }
 
+  /**
+   * method for reporting string values parsed/processed during tests
+   */
+
   protected static void testoutput(BootstrapArgs bsa, Arg a, String s1,
           String s2)
   {
@@ -1849,6 +1857,9 @@ public class Jalview
     testoutput(true, a, s1, s2);
   }
 
+  /**
+   * report value set for string values parsed/processed during tests
+   */
   private static void testoutput(boolean yes, Arg a, String s1, String s2)
   {
     if (yes && ((s1 == null && s2 == null)
index f7588cb..2f25978 100644 (file)
@@ -84,7 +84,7 @@ public enum Arg
           "Specifies the title for the open alignment window as string.",
           Opt.STRING, Opt.LINKED),
   COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
-          "Applies the colour scheme to the open alignment window. Valid values are:\n"
+          "Applies the colour scheme to the open alignment window. Valid values include:\n"
                   + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
                   + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
                   + "gecos-blossom,\n" + "gecos-sunset,\n"
@@ -93,7 +93,11 @@ public enum Arg
                   + "turn-propensity,\n" + "buried-index,\n"
                   + "nucleotide,\n" + "nucleotide-ambiguity,\n"
                   + "purine-pyrimidine,\n" + "rna-helices,\n"
-                  + "t-coffee-scores,\n" + "sequence-id.",
+                  + "t-coffee-scores,\n" + "sequence-id.\n"
+                  +"\n"
+                  + "Names of user defined colourschemes will also work,\n"
+                 +"and jalview colourscheme specifications like\n"
+                  +"--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
   FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
           Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
index 1558065..907b1fa 100644 (file)
@@ -52,77 +52,111 @@ public class ArgParser
 
   protected static final String NEGATESTRING = "no";
 
-  // the default linked id prefix used for no id (not even square braces)
+  /**
+   * the default linked id prefix used for no id (ie when not even square braces
+   * are provided)
+   */
   protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
 
-  // the linkedId string used to match all linkedIds seen so far
+  /**
+   * the linkedId string used to match all linkedIds seen so far
+   */
   protected static final String MATCHALLLINKEDIDS = "*";
 
-  // the linkedId string used to match all of the last --open'ed linkedIds
+  /**
+   * the linkedId string used to match all of the last --open'ed linkedIds
+   */
   protected static final String MATCHOPENEDLINKEDIDS = "open*";
 
-  // the counter added to the default linked id prefix
+  /**
+   * the counter added to the default linked id prefix
+   */
   private int defaultLinkedIdCounter = 0;
 
-  // the substitution string used to use the defaultLinkedIdCounter
+  /**
+   * the substitution string used to use the defaultLinkedIdCounter
+   */
   private static final String DEFAULTLINKEDIDCOUNTER = "{}";
 
-  // the counter added to the default linked id prefix. NOW using
-  // linkedIdAutoCounter
-  // private int openLinkedIdCounter = 0;
-
-  // the linked id prefix used for --open files. NOW the same as DEFAULT
+  /**
+   * the linked id prefix used for --open files. NOW the same as DEFAULT
+   */
   protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
 
-  // the counter used for {n} substitutions
+  /**
+   * the counter used for {n} substitutions
+   */
   private int linkedIdAutoCounter = 0;
 
-  // the linked id substitution string used to increment the idCounter (and use
-  // the incremented value)
+  /**
+   * the linked id substitution string used to increment the idCounter (and use
+   * the incremented value)
+   */
   private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
 
-  // the linked id substitution string used to use the idCounter
+  /**
+   * the linked id substitution string used to use the idCounter
+   */
   private static final String LINKEDIDAUTOCOUNTER = "{n}";
 
-  // the linked id substitution string used to use the filename extension of
-  // --append
-  // or --open
+  /**
+   * the linked id substitution string used to use the filename extension of
+   * --append or --open
+   */
   private static final String LINKEDIDEXTENSION = "{extension}";
 
-  // the linked id substitution string used to use the base filename of --append
-  // or --open
+  /**
+   * the linked id substitution string used to use the base filename of --append
+   */
+  /** or --open */
   private static final String LINKEDIDBASENAME = "{basename}";
 
-  // the linked id substitution string used to use the dir path of --append
-  // or --open
+  /**
+   * the linked id substitution string used to use the dir path of --append or
+   * --open
+   */
   private static final String LINKEDIDDIRNAME = "{dirname}";
 
-  // the current argfile
+  /**
+   * the current argfile
+   */
   private String argFile = null;
 
-  // the linked id substitution string used to use the dir path of the latest
-  // --argfile name
+  /**
+   * the linked id substitution string used to use the dir path of the latest
+   */
+  /** --argfile name */
   private static final String ARGFILEBASENAME = "{argfilebasename}";
 
-  // the linked id substitution string used to use the dir path of the latest
-  // --argfile name
+  /**
+   * the linked id substitution string used to use the dir path of the latest
+   * --argfile name
+   */
   private static final String ARGFILEDIRNAME = "{argfiledirname}";
 
-  // flag to say whether {n} subtitutions in output filenames should be made.
-  // Turn on and off with --substitutions and --nosubstitutions
-  // Start with it on
+  /**
+   * flag to say whether {n} subtitutions in output filenames should be made.
+   * Turn on and off with --substitutions and --nosubstitutions Start with it on
+   */
   private boolean substitutions = true;
 
-  // flag to say whether the default linkedId is the current default linked id
-  // or ALL linkedIds
+  /**
+   * flag to say whether the default linkedId is the current default linked id
+   *
+   * or ALL linkedIds
+   */
   private boolean allLinkedIds = false;
 
-  // flag to say whether the default linkedId is the current default linked id
-  // or OPENED linkedIds
+  /**
+   * flag to say whether the default linkedId is the current default linked id
+   * or OPENED linkedIds
+   */
   private boolean openedLinkedIds = false;
 
-  // flag to say whether the structure arguments should be applied to all
-  // structures with this linked id
+  /**
+   * flag to say whether the structure arguments should be applied to all
+   * structures with this linked id
+   */
   private boolean allStructures = false;
 
   protected static final Map<String, Arg> argMap;
@@ -925,15 +959,19 @@ public class ArgParser
     FIRST, BEFORE, AFTER
   }
 
-  // get from following Arg of type a or subval of same name (lowercase)
+  /**
+   * get from following Arg of type a or subval of same name (lowercase)
+   */
   public static String getValueFromSubValOrArg(ArgValuesMap avm,
           ArgValue av, Arg a, SubVals sv)
   {
     return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
   }
 
-  // get from following Arg of type a or subval key or preference pref or
-  // default def
+  /**
+   * get from following Arg of type a or subval key or preference pref or
+   * default def
+   */
   public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
           Arg a, SubVals sv, String key, String pref, String def)
   {
@@ -941,8 +979,10 @@ public class ArgParser
             def);
   }
 
-  // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
-  // Arg of type a or subval key or preference pref or default def
+  /**
+   * get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
+   * Arg of type a or subval key or preference pref or default def
+   */
   public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
           Position pos, ArgValue av, SubVals sv, String key, String pref,
           String def)
@@ -1071,7 +1111,9 @@ public class ArgParser
 
   // the following methods look for the "*" linkedId and add the argvalue to all
   // linkedId ArgValues if it does.
-  // This version inserts the subvals sv into all created values
+  /**
+   * This version inserts the subvals sv into all created values
+   */
   private void addValue(String linkedId, Type type, ArgValues avs,
           SubVals sv, String v, int argIndex, boolean doSubs)
   {
@@ -1119,21 +1161,32 @@ public class ArgParser
             doSubs);
   }
 
-  /*
+  /**
    * The following operations look for the "*" and "open*" linkedIds and add the
-   * argvalue to all appropriate linkedId ArgValues if it does.
-   * If subvals are supplied, they are inserted into all new set values.
+   * argvalue to all appropriate linkedId ArgValues if it does. If subvals are
+   * supplied, they are inserted into all new set values.
    * 
-   * @param op The ArgParser.Op operation
-   * @param linkedId The String linkedId from the ArgValuesMap
-   * @param type The Arg.Type to attach to this ArgValue
-   * @param avs The ArgValues for this linkedId
-   * @param sv Use these SubVals on the ArgValue
-   * @param merge Merge the SubVals with any existing on the value.  False will replace unless sv is null
-   * @param v The value of the ArgValue (may contain subvals).
-   * @param b The boolean value of the ArgValue.
-   * @param argIndex The argIndex for the ArgValue.
-   * @param doSubs Whether to perform substitutions on the subvals and value.
+   * @param op
+   *          The ArgParser.Op operation
+   * @param linkedId
+   *          The String linkedId from the ArgValuesMap
+   * @param type
+   *          The Arg.Type to attach to this ArgValue
+   * @param avs
+   *          The ArgValues for this linkedId
+   * @param sv
+   *          Use these SubVals on the ArgValue
+   * @param merge
+   *          Merge the SubVals with any existing on the value. False will
+   *          replace unless sv is null
+   * @param v
+   *          The value of the ArgValue (may contain subvals).
+   * @param b
+   *          The boolean value of the ArgValue.
+   * @param argIndex
+   *          The argIndex for the ArgValue.
+   * @param doSubs
+   *          Whether to perform substitutions on the subvals and value.
    */
   private void argValueOperation(Op op, String linkedId, Type type,
           ArgValues avs, SubVals sv, boolean merge, String v, boolean b,
index 6f2faae..1fcc591 100644 (file)
@@ -3571,4 +3571,17 @@ public class Desktop extends jalview.jbgui.GDesktop
       jalview.bin.Console.debug(Cache.getStackTraceString(e));
     }
   }
+
+  /**
+   * closes the current instance window, disposes and forgets about it.
+   */
+  public static void closeDesktop()
+  {
+    if (Desktop.instance != null) {
+      Desktop.instance.closeAll_actionPerformed(null);
+      Desktop.instance.setVisible(false);
+      Desktop.instance.dispose();
+      Desktop.instance = null;
+    }
+  }
 }
index 32704d6..e18ca86 100644 (file)
@@ -220,8 +220,8 @@ public class ImageExporter
               messageId);
     } catch (Exception e)
     {
-      System.out.println(String.format("Error creating %s file: %s", type,
-              e.toString()));
+      jalview.bin.Console.error(String.format("Error creating %s file: %s", type,
+              e.toString()),e);
       setStatus(MessageManager.formatMessage("info.error_creating_file",
               type), messageId);
     }
index ed80eb9..d659e2a 100644 (file)
@@ -34,6 +34,7 @@ import jalview.datamodel.AlignExportSettingsAdapter;
 import jalview.datamodel.AlignmentExportData;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.IProgressIndicator;
+import jalview.io.exceptions.ImageOutputException;
 import jalview.util.MessageManager;
 
 public abstract class HTMLOutput implements Runnable
@@ -302,12 +303,12 @@ public abstract class HTMLOutput implements Runnable
     return generatedFile;
   }
 
-  public void exportHTML(String outputFile)
+  public void exportHTML(String outputFile) throws ImageOutputException
   {
     exportHTML(outputFile, null);
   }
 
-  public void exportHTML(String outputFile, String renderer)
+  public void exportHTML(String outputFile, String renderer) throws ImageOutputException
   {
     setProgressMessage(MessageManager.formatMessage(
             "status.exporting_alignment_as_x_file", getDescription()));
@@ -358,5 +359,5 @@ public abstract class HTMLOutput implements Runnable
   }
 
   // used to pass an option such as render to run
-  public abstract void run(String string);
+  public abstract void run(String string) throws ImageOutputException;
 }
\ No newline at end of file
diff --git a/src/jalview/io/exceptions/ImageOutputException.java b/src/jalview/io/exceptions/ImageOutputException.java
new file mode 100644 (file)
index 0000000..bf06494
--- /dev/null
@@ -0,0 +1,36 @@
+package jalview.io.exceptions;
+
+/**
+ * wrapper for passing error messages and exceptions back to UI when image io goes wrong
+ * @author jprocter
+ *
+ */
+public class ImageOutputException extends Exception
+{
+
+  public ImageOutputException()
+  {
+  }
+
+  public ImageOutputException(String message)
+  {
+    super(message);
+  }
+
+  public ImageOutputException(Throwable cause)
+  {
+    super(cause);
+  }
+
+  public ImageOutputException(String message, Throwable cause)
+  {
+    super(message, cause);
+  }
+
+  public ImageOutputException(String message, Throwable cause,
+          boolean enableSuppression, boolean writableStackTrace)
+  {
+    super(message, cause, enableSuppression, writableStackTrace);
+  }
+
+}
index 08ef3a2..d81db8c 100644 (file)
@@ -200,11 +200,20 @@ public class AlignCalcManager implements AlignCalcManagerI
   @Override
   public boolean isWorking()
   {
+    boolean working=false;
     synchronized (inProgress)
     {
       // System.err.println("isWorking "+hashCode());
-      return inProgress.size() > 0;
+      working |= inProgress.size() > 0;
     }
+    synchronized (updating)
+    {
+      for (List<AlignCalcWorkerI> workers : updating.values())
+      {
+        working |= workers.size() > 0;
+      }
+    }
+    return working;
   }
 
   @Override
index b4c927b..61892df 100644 (file)
@@ -49,8 +49,15 @@ public class CommandsTest
   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
-    if (Desktop.instance != null)
-      Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.closeDesktop();
+  }
+  
+  public static void callJalviewMain(String[] args) {
+    if (Jalview.getInstance()!=null) {
+      Jalview.getInstance().doMain(args);
+    } else {
+      Jalview.main(args);
+    }
   }
 
   /* --setprops is currently disabled so this test won't work
@@ -71,7 +78,7 @@ public class CommandsTest
           int numFrames, String[] sequences)
   {
     String[] args = (cmdLine + " --gui").split("\\s+");
-    Jalview.main(args);
+    callJalviewMain(args);
     Commands cmds = Jalview.getInstance().getCommands();
     Assert.assertNotNull(cmds);
     Assert.assertEquals(cmds.commandArgsProvided(), cmdArgs,
@@ -111,7 +118,7 @@ public class CommandsTest
   {
     cleanupFiles(filenames);
     String[] args = (cmdLine + " --gui").split("\\s+");
-    Jalview.main(args);
+    callJalviewMain(args);
     Commands cmds = Jalview.getInstance().getCommands();
     Assert.assertNotNull(cmds);
     File lastFile = null;
@@ -139,7 +146,7 @@ public class CommandsTest
   {
     cleanupFiles(filenames);
     String[] args = (cmdLine + " --gui").split("\\s+");
-    Jalview.main(args);
+    callJalviewMain(args);
     Commands cmds = Jalview.getInstance().getCommands();
     Assert.assertNotNull(cmds);
     File lastFile = null;
@@ -297,7 +304,7 @@ public class CommandsTest
           String[] nonfilenames)
   {
     String[] args = (cmdLine + " --gui").split("\\s+");
-    Jalview.main(args);
+    callJalviewMain(args);
     Commands cmds = Jalview.getInstance().getCommands();
     Assert.assertNotNull(cmds);
     for (String filename : filenames)
index 0a47700..0c2071a 100644 (file)
@@ -51,8 +51,7 @@ public class CommandsTest2
   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
-    if (Desktop.instance != null)
-      Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.closeDesktop();
   }
 
   @Test(
@@ -65,7 +64,7 @@ public class CommandsTest2
   {
     String[] args = cmdLine.split("\\s+");
 
-    Jalview.main(args);
+    CommandsTest.callJalviewMain(args);
     try
     {
       // sleep for slow build server to open annotations and viewer windows