JAL-629 Added --structureimage code and formatting args/subvals. Added tests for...
authorBen Soares <b.soares@dundee.ac.uk>
Tue, 23 May 2023 13:33:38 +0000 (14:33 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Tue, 23 May 2023 13:33:38 +0000 (14:33 +0100)
14 files changed:
help/help/html/features/clarguments.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/bin/argparser/ArgValue.java
src/jalview/bin/argparser/ArgValuesMap.java
src/jalview/bin/argparser/SubVals.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmol.java
src/jalview/gui/StructureChooser.java
test/jalview/bin/CommandsTest.java
test/jalview/bin/CommandsTest2.java
test/jalview/bin/argparser/ArgParserTest.java

index 437392f..d1ee915 100644 (file)
     <td align="center">&#x2713;</td>
     </tr>
 
+    <tr valign="top">
+    <td><code>&#8209;&#8209;close</code></td>
+    <td>Close the open alignment window.  This occurs after other output, processing and image export arguments.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
+  </table>
+
+
+  <h2>Processing alignments</h2>
+
+  <table border="1" cellpadding="3">
+    <tr valign="top">
+    <td><strong>argument</strong></td>
+    <td><strong>action</strong></td>
+    <td><strong>sub-value modifiers</strong> (optional)</td>
+    <td><strong>linked</strong> (optional)</td>
+    </tr>
+
+    <tr valign="top">
+    <td><code>&#8209;&#8209;groovy&nbsp;<em>filename</em></code></td>
+    <td>Process a groovy script in the file for the open alignment.</td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
   </table>
 
 
-  <h2>Outputting files</h2>
+  <h2>Outputting alignment files</h2>
 
   <table border="1" cellpadding="3">
     <tr valign="top">
     </tr>
 
     <tr valign="top">
+    <td><code>&#8209;&#8209;backups / &#8209;&#8209;nobackups</code></td>
+    <td>Enable (or disable) writing backup files when saving an <code>&#8209;&#8209;output</code> file.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
+    <tr valign="top">
+    <td><code>&#8209;&#8209;overwrite / &#8209;&#8209;nooverwrite</code></td>
+    <td>Enable (or disable) overwriting of output files without backups enabled.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
+  </table>
+
+
+  <h2>Exporting image files</h2>
+
+  <table border="1" cellpadding="3">
+    <tr valign="top">
+    <td><strong>argument</strong></td>
+    <td><strong>action</strong></td>
+    <td><strong>sub-value modifiers</strong> (optional)</td>
+    <td><strong>linked</strong> (optional)</td>
+    </tr>
+
+    <tr valign="top">
     <td><code>&#8209;&#8209;image&nbsp;<em>filename</em></code></td>
     <td>Output an image of the open alignment window.  Format is specified by the sub-value modifier, a following <code>&#8209;&#8209;type</code> argument or guessed from the file extension.  Valid formats/extensions are:
     <br/>
 
     <tr valign="top">
     <td><code>&#8209;&#8209;scale&nbsp;<em>number</em></code></td>
-    <td>Sets a scaling for bitmap image format (PNG).  Should be given as a floating point number.  This can also be set as a sub-value modifier to the <code>--image</code> value.  If used in conjunction with <code>--width</code> and <code>--height</code> then the smallest scaling will be used (<code>scale</code>, <code>width</code> and <code>height</code> provide bounds for the image).</td>
+    <td>Sets a scaling for bitmap image format (PNG).  Should be given as a floating point number.  This can also be set as a sub-value modifier to the <code>--image</code> value.  If used in conjunction with <code>--width</code> and <code>--height</code> then the smallest scaling will be used (<code>scale</code>, <code>width</code> and <code>height</code> provide bounds for the image).
+    </td>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
 
     <tr valign="top">
     <td><code>&#8209;&#8209;width&nbsp;<em>number</em></code></td>
-    <td>Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio.  Should be given as a positive integer.  This can also be set as a sub-value modifier to the <code>--image</code> value.  If used in conjunction with <code>--scale</code> and <code>--height</code> then the smallest scaling will be used (<code>scale</code>, <code>width</code> and <code>height</code> provide bounds for the image).</td>
+    <td>Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio.  Should be given as a positive integer.  This can also be set as a sub-value modifier to the <code>--image</code> value.  If used in conjunction with <code>--scale</code> and <code>--height</code> then the smallest scaling will be used (<code>scale</code>, <code>width</code> and <code>height</code> provide bounds for the image).
+    </td>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
     <td align="center">&#x2713;</td>
     </tr>
 
+  </table>
+
+
+  <h2>Exporting 3D structure image files (<code>jmol</code> only)</h2>
+
+  <table border="1" cellpadding="3">
+    <tr valign="top">
+    <td><strong>argument</strong></td>
+    <td><strong>action</strong></td>
+    <td><strong>sub-value modifiers</strong> (optional)</td>
+    <td><strong>linked</strong> (optional)</td>
+    </tr>
+
+    <tr valign="top">
+    <td><code>&#8209;&#8209;structureimage&nbsp;<em>filename</em></code></td>
+    <td>Export an image of a 3D structure opened in JMOL.  Image formats can be:
+    <br/>
+    <code>svg</code>,
+    <br/>
+    <code>png</code>,
+    <br/>
+    <code>eps</code>.
+    </td>
+    <td>
+      <code>structureimagetype=<em>name</em>,
+      <code>structureimagetextrenderer=<em>name</em>,
+      <code>structureimagescale=<em>number</em>,
+      <code>structureimagewidth=<em>number</em>,
+      <code>structureimageheight=<em>number</em>
+    </td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
+    <tr valign="top">
+    <td><code>&#8209;&#8209;structureimagetype&nbsp;<em>name</em></code></td>
+    <td>Set the structure image format for the preceding --structureimage. Valid values are:
+    <br/>
+    <code>svg</code>,
+    <br/>
+    <code>png</code>,
+    <br/>
+    <code>eps</code>.
+    </td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
     <tr valign="top">
-    <td><code>&#8209;&#8209;backups / &#8209;&#8209;nobackups</code></td>
-    <td>Enable (or disable) writing backup files when saving an <code>&#8209;&#8209;output</code> file.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td><code>&#8209;&#8209;structureimagetextrenderer&nbsp;<em>name</em></code></td>
+    <td>Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:
+    <br/>
+    <code>text</code>,
+    <br/>
+    <code>lineart</code>.
+    </td>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
 
     <tr valign="top">
-    <td><code>&#8209;&#8209;overwrite / &#8209;&#8209;nooverwrite</code></td>
-    <td>Enable (or disable) overwriting of output files without backups enabled.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td><code>&#8209;&#8209;structureimagescale&nbsp;<em>number</em></code></td>
+    <td>Sets a scaling for bitmap structure image format (PNG). Should be given as a floating point number. If used in conjunction with --structureimagewidth and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).
+    </td>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
 
     <tr valign="top">
-    <td><code>&#8209;&#8209;close</code></td>
-    <td>Close the current open alignment window.  This occurs after other output arguments.  This applies to the current open alignment -- to apply to all <code>&#8209;&#8209;output</code> and <code>&#8209;&#8209;image</code> files, use after <code>&#8209;&#8209;all</code>.</td>
+    <td><code>&#8209;&#8209;structureimagewidth&nbsp;<em>number</em></code></td>
+    <td>Sets a width for bitmap structure image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).
+    </td>
+    <td></td>
+    <td align="center">&#x2713;</td>
+    </tr>
+
+    <tr valign="top">
+    <td><code>&#8209;&#8209;structureimageheight&nbsp;<em>number</em></code></td>
+    <td>Sets a height for bitmap structure image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimagewidth then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).
+    </td>
     <td></td>
     <td align="center">&#x2713;</td>
     </tr>
     </tr>
 
     <tr valign="top">
+    <td><code>&#8209;&#8209;allstructures / &#8209;&#8209;noallstructures</code></td>
+    <td>
+        Apply (or stop applying) the following 3D structure formatting arguments to all structures <em>within the current open alignment</em>.  Whilst <code>--allstructures</code> will continue to operate for a <code>--new</code> alignment, the structure formatting arguments must be set again for each new alignment.
+    </td>
+    </tr>
+
+    <tr valign="top">
     <td><code>&#8209;&#8209;quit</code></td>
     <td>After all files have been opened, appended and output, quit Jalview.  In <code>&#8209;&#8209;headless</code> mode this already happens.</td>
     </tr>
index 2d57bc4..3437bfd 100644 (file)
@@ -19,7 +19,6 @@ import jalview.bin.argparser.Arg;
 import jalview.bin.argparser.ArgParser;
 import jalview.bin.argparser.ArgParser.Position;
 import jalview.bin.argparser.ArgValue;
-import jalview.bin.argparser.ArgValues;
 import jalview.bin.argparser.ArgValuesMap;
 import jalview.bin.argparser.SubVals;
 import jalview.datamodel.AlignmentI;
@@ -502,8 +501,9 @@ public class Commands
                   .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
                           Arg.TEMPFAC, Position.AFTER, av, subVals, null,
                           null, null);
-          boolean notempfac = ArgParser.getBoolFromSubValOrArg(avm,
-                  Arg.NOTEMPFAC, subVals);
+          boolean notempfac = ArgParser.getFromSubValArgOrPref(avm,
+                  Arg.NOTEMPFAC, subVals, null, "ADD_TEMPFACT_ANN", false,
+                  true);
           TFType tft = notempfac ? null : TFType.DEFAULT;
           /*
           String tftString = subVals.get("tempfac");
@@ -559,10 +559,6 @@ public class Commands
             }
           }
 
-          boolean addTempFac = notempfac ? false
-                  : ((tft != null)
-                          || Cache.getDefault("ADD_TEMPFACT_ANN", false));
-
           // TODO use ssFromStructure
           StructureViewer sv = StructureChooser
                   .openStructureFileForSequence(null, null, ap, seq, false,
@@ -606,7 +602,7 @@ public class Commands
             }
             BitmapImageSizing userBis = ImageMaker
                     .parseScaleWidthHeightStrings(scale, width, height);
-            switch (sv.getViewerType())
+            switch (StructureViewer.getViewerType())
             {
             case JMOL:
               try
@@ -909,9 +905,15 @@ public class Commands
     SequenceI seq = null;
     if (subVals == null && idAv == null)
       return null;
+    if (af == null || af.getCurrentView() == null)
+    {
+      return null;
+    }
     AlignmentI al = af.getCurrentView().getAlignment();
     if (al == null)
+    {
       return null;
+    }
     if (subVals != null)
     {
       if (subVals.has(Arg.SEQID.getName()))
@@ -930,54 +932,4 @@ public class Commands
     }
     return seq;
   }
-
-  // returns the first Arg value intended for the structure structFilename
-  // (in the given AlignFrame from the ArgValuesMap)
-  private ArgValue getArgAssociatedWithStructure(Arg arg, ArgValuesMap avm,
-          AlignFrame af, String structFilename)
-  {
-    if (af != null)
-    {
-      for (ArgValue av : avm.getArgValueList(arg))
-      {
-        SubVals subVals = av.getSubVals();
-        String structid = subVals.get("structid");
-        String structfile = subVals.get("structfile");
-
-        // let's find a structure
-        if (structfile == null && structid == null)
-        {
-          ArgValue likelyStructure = avm.getClosestPreviousArgValueOfArg(av,
-                  Arg.STRUCTURE);
-          if (likelyStructure != null)
-          {
-            SubVals sv = likelyStructure.getSubVals();
-            if (sv != null && sv.has(ArgValues.ID))
-            {
-              structid = sv.get(ArgValues.ID);
-            }
-            else
-            {
-              structfile = likelyStructure.getValue();
-            }
-          }
-        }
-
-        if (structfile == null && structid != null)
-        {
-          StructureSelectionManager ssm = StructureSelectionManager
-                  .getStructureSelectionManager(Desktop.instance);
-          if (ssm != null)
-          {
-            structfile = ssm.findFileForPDBId(structid);
-          }
-        }
-        if (structfile != null && structfile.equals(structFilename))
-        {
-          return av;
-        }
-      }
-    }
-    return null;
-  }
 }
index 2716ce1..4fe822d 100755 (executable)
@@ -774,7 +774,17 @@ public class Jalview
     {
       if (headlessArg)
       {
-        Jalview.exit("Successfully completed commands in headless mode", 0);
+        if (argparser.getBoolean(Arg.NOQUIT))
+        {
+          Console.warn(
+                  "Completed " + Arg.HEADLESS.getName() + " commands, but "
+                          + Arg.NOQUIT + " is set so not quitting!");
+        }
+        else
+        {
+          Jalview.exit("Successfully completed commands in headless mode",
+                  0);
+        }
       }
       Console.info("Successfully completed commands");
     }
index 2354cdd..8bac623 100644 (file)
@@ -131,8 +131,7 @@ public enum Arg
   NOTEMPFAC(Type.STRUCTURE,
           "Do not show the temperature factor annotation for the preceding --structure.",
           Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
-                                                            // until it
-  // works!
+                                                            // until it works!
   SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
           Opt.ALLOWALL),
 
@@ -159,25 +158,24 @@ public enum Arg
   HEIGHT(Type.IMAGE,
           "Sets a height for bitmap image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --width then the smallest scaling will be used (scale, width and height provide bounds for the image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  STRUCTUREIMAGE(Type.IMAGE,
+  STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
           "Export an image of a 3D structure opened in JMOL", Opt.STRING,
           Opt.LINKED, Opt.MULTI),
-
-  STRUCTUREIMAGETYPE(Type.IMAGE,
+  STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
           "Set the structure image format for the preceding --structureimage. Valid values are:\n"
                   + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  STRUCTUREIMAGETEXTRENDERER(Type.IMAGE,
-          "Sets whether text in a vector structure image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
+  STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
+          "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
                   + "text,\n" + "lineart.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  STRUCTUREIMAGESCALE(Type.IMAGE,
+  STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
           "Sets a scaling for bitmap structure image format (PNG). Should be given as a floating point number. If used in conjunction with --structureimagewidth and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  STRUCTUREIMAGEWIDTH(Type.IMAGE,
+  STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
           "Sets a width for bitmap structure image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
-  STRUCTUREIMAGEHEIGHT(Type.IMAGE,
+  STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
           "Sets a height for bitmap structure image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimagewidth then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
           Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
 
@@ -243,6 +241,12 @@ public enum Arg
   QUIT(Type.FLOW,
           "After all files have been opened, appended and output, quit Jalview. In â€‘‑headless mode this already happens.",
           Opt.UNARY),
+  NOQUIT(Type.FLOW,
+          "Secret arg to not quit after --headless mode for tests",
+          Opt.UNARY, Opt.SECRET),
+  ALLSTRUCTURES(Type.FLOW,
+          "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
+          Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
 
   // secret options
   TESTOUTPUT(Type.CONFIG,
@@ -342,6 +346,7 @@ public enum Arg
     PROCESS("arguments used to process an alignment once opened"),
     OUTPUT("arguments used to save data from a processed alignment"),
     IMAGE("arguments used to export an image of an alignment"),
+    STRUCTUREIMAGE("arguments used to export an image of an structure"),
     FLOW("arguments that control processing of the other arguments"), //
     ALL("all arguments"), // mostly just a place-holder for --help-all
     NONE, // mostly a place-holder for --help
index 6d1251c..0a8b8d3 100644 (file)
@@ -121,6 +121,10 @@ public class ArgParser
   // or OPENED linkedIds
   private boolean openedLinkedIds = false;
 
+  // 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;
 
   protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
@@ -424,6 +428,10 @@ public class ArgParser
           openedLinkedIds = !negated;
           allLinkedIds = false;
         }
+        else if (a == Arg.ALLSTRUCTURES)
+        {
+          allStructures = !negated;
+        }
 
         if (a.hasOption(Opt.STORED))
         {
@@ -485,23 +493,26 @@ public class ArgParser
                       + arg);
             }
           }
-          else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
-          {
-            // turn {n} to the autoCounter
-            autoCounterString = Integer.toString(linkedIdAutoCounter);
-            linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
-                    autoCounterString);
-            Console.debug(
-                    "Changing linkedId to '" + linkedId + "' from " + arg);
-          }
-          else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
+          else
           {
-            // turn {++n} to the incremented autoCounter
-            autoCounterString = Integer.toString(++linkedIdAutoCounter);
-            linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
-                    autoCounterString);
-            Console.debug(
-                    "Changing linkedId to '" + linkedId + "' from " + arg);
+            if (linkedId.contains(LINKEDIDAUTOCOUNTER))
+            {
+              // turn {n} to the autoCounter
+              autoCounterString = Integer.toString(linkedIdAutoCounter);
+              linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
+                      autoCounterString);
+              Console.debug("Changing linkedId to '" + linkedId + "' from "
+                      + arg);
+            }
+            if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
+            {
+              // turn {++n} to the incremented autoCounter
+              autoCounterString = Integer.toString(++linkedIdAutoCounter);
+              linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
+                      autoCounterString);
+              Console.debug("Changing linkedId to '" + linkedId + "' from "
+                      + arg);
+            }
           }
         }
 
@@ -521,8 +532,9 @@ public class ArgParser
         }
 
         // check for unique id
-        SubVals idsv = new SubVals(val);
-        String id = idsv.get(ArgValues.ID);
+        SubVals subvals = new SubVals(val);
+        boolean addNewSubVals = false;
+        String id = subvals.get(ArgValues.ID);
         if (id != null && avm.hasId(a, id))
         {
           Console.error("Argument '" + a.argString()
@@ -530,10 +542,21 @@ public class ArgParser
           continue;
         }
 
-        /* TODO
-         * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
-         * DONE, need to check
-         */
+        // set allstructures to all non-primary structure options in this linked
+        // id if --allstructures has been set
+        if (allStructures
+                && (a.getType() == Type.STRUCTURE
+                        || a.getType() == Type.STRUCTUREIMAGE)
+                && !a.hasOption(Opt.PRIMARY))
+        {
+          if (!subvals.has(Arg.ALLSTRUCTURES.getName()))
+          // && !subvals.has("structureid"))
+          {
+            subvals.put(Arg.ALLSTRUCTURES.getName(), "true");
+            addNewSubVals = true;
+          }
+        }
+
         ArgValues avs = avm.getOrCreateArgValues(a);
 
         // store appropriate String value(s)
@@ -563,7 +586,9 @@ public class ArgParser
           }
           else
           {
-            addValue(linkedId, type, avs, val, argIndex, true);
+            // addValue(linkedId, type, avs, val, argIndex, true);
+            addValue(linkedId, type, avs, addNewSubVals ? subvals : null,
+                    val, argIndex, true);
           }
         }
         else if (a.hasOption(Opt.BOOLEAN))
@@ -925,8 +950,23 @@ public class ArgParser
       else if (pos == Position.AFTER
               && avm.getClosestNextArgValueOfArg(av, a) != null)
         value = avm.getClosestNextArgValueOfArg(av, a).getValue();
+
+      // look for allstructures subval for Type.STRUCTURE*
+      Arg arg = av.getArg();
+      if (value == null && arg.hasOption(Opt.PRIMARY)
+              && arg.getType() == Type.STRUCTURE
+              && !a.hasOption(Opt.PRIMARY) && (a.getType() == Type.STRUCTURE
+                      || a.getType() == Type.STRUCTUREIMAGE))
+      {
+        ArgValue av2 = avm.getArgValueOfArgWithSubValKey(a,
+                Arg.ALLSTRUCTURES.getName());
+        if (av2 != null)
+        {
+          value = av2.getValue();
+        }
+      }
     }
-    else
+    if (value == null)
     {
       value = pref != null ? Cache.getDefault(pref, def) : def;
     }
@@ -942,6 +982,13 @@ public class ArgParser
   public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
           SubVals sv, String key, String pref, boolean def)
   {
+    return getFromSubValArgOrPref(avm, a, sv, key, pref, def, false);
+  }
+
+  public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
+          SubVals sv, String key, String pref, boolean def,
+          boolean invertPref)
+  {
     if ((key == null && a == null) || (sv == null && a == null))
       return false;
 
@@ -995,7 +1042,8 @@ public class ArgParser
       return avm.getBoolean(a);
 
     // return preference or default
-    return pref != null ? Cache.getDefault(pref, def) : def;
+    boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false;
+    return pref != null ? (invertPref ? !prefVal : prefVal) : def;
   }
 
   // the following methods look for the "*" linkedId and add the argvalue to all
@@ -1039,13 +1087,35 @@ public class ArgParser
     ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
   }
 
-  // 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.
   private void argValueOperation(Op op, String linkedId, Type type,
           ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
           boolean doSubs)
   {
+    // default to merge subvals if subvals are provided
+    argValueOperation(op, linkedId, type, avs, sv, true, v, b, argIndex,
+            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.
+   * 
+   * @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,
+          int argIndex, boolean doSubs)
+  {
     Arg a = avs.arg();
 
     List<String> wildcardLinkedIds = null;
@@ -1097,8 +1167,8 @@ public class ArgParser
           {
             if (doSubs)
             {
-              val = makeSubstitutions(v, id);
-              sv = new SubVals(sv, val);
+              sv = new SubVals(sv, val, merge);
+              val = makeSubstitutions(sv.getContent(), id);
             }
             tavs.addValue(sv, type, val, argIndex, true);
           }
index f76b757..3467f61 100644 (file)
@@ -19,8 +19,10 @@ public class ArgValue implements Comparable<ArgValue>
    */
   private Type type = null;
 
-  // This id is set by a subVal id= to identify the product of this ArgValue
-  // later. Set but not currently used.
+  /*
+   * This id is set by a subVal id= to identify the product of this ArgValue
+   * later. Set but not currently used.
+   */
   private String id;
 
   private SubVals subVals;
index 085099a..ab6fcc1 100644 (file)
@@ -120,6 +120,33 @@ public class ArgValuesMap
     return m.keySet();
   }
 
+  public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey)
+  {
+    return getArgValueOfArgWithSubValKey(a, svKey, false);
+  }
+
+  public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey,
+          boolean last)
+  {
+    ArgValues avs = this.getArgValues(a);
+    if (avs == null)
+    {
+      return null;
+    }
+    List<ArgValue> compareAvs = avs.getArgValueList();
+    for (int i = 0; i < compareAvs.size(); i++)
+    {
+      int index = last ? compareAvs.size() - 1 - i : i;
+      ArgValue av = compareAvs.get(index);
+      SubVals sv = av.getSubVals();
+      if (sv.has(svKey) && !sv.get(svKey).equals("false"))
+      {
+        return av;
+      }
+    }
+    return null;
+  }
+
   public ArgValue getClosestPreviousArgValueOfArg(ArgValue thisAv, Arg a)
   {
     ArgValue closestAv = null;
@@ -161,6 +188,7 @@ public class ArgValuesMap
     return closestAv;
   }
 
+  // TODO this is incomplete and currently unused (fortunately)
   public ArgValue[] getArgValuesReferringTo(String key, String value, Arg a)
   {
     // this looks for the *next* arg that *might* be referring back to
index a03ec15..4d146d9 100644 (file)
@@ -29,14 +29,42 @@ public class SubVals
 
   protected SubVals(SubVals sv, String c)
   {
-    if (sv == null)
+    this(sv, c, true);
+  }
+
+  protected SubVals(SubVals sv, String c, boolean merge)
+  {
+    SubVals subvals;
+    if (merge)
+    {
+      SubVals vsv = new SubVals(c);
+      if (sv != null && sv.getSubValMap() != null)
+      {
+        for (String key : sv.getSubValMap().keySet())
+        {
+          vsv.put(key, sv.get(key));
+        }
+      }
+      if (sv != null && sv.getIndex() > 0)
+      {
+        vsv.index = sv.getIndex();
+      }
+      subvals = vsv;
+    }
+    else
+    {
+      // replace
+      subvals = sv;
+    }
+    if (subvals == null)
     {
       this.subValMap = new HashMap<>();
     }
     else
     {
-      this.subValMap = sv == null ? new HashMap<>() : sv.getSubValMap();
-      this.index = sv.getIndex();
+      this.subValMap = subvals == null ? new HashMap<>()
+              : subvals.getSubValMap();
+      this.index = subvals.getIndex();
     }
     this.content = c;
   }
index 37eaae7..160ee1a 100644 (file)
@@ -982,7 +982,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void setProgressBar(String message, long id)
   {
-    if (!Platform.isHeadless())
+    if (!Platform.isHeadless() && progressBar != null)
       progressBar.setProgressBar(message, id);
   }
 
@@ -990,7 +990,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void registerHandler(final long id,
           final IProgressIndicatorHandler handler)
   {
-    progressBar.registerHandler(id, handler);
+    if (progressBar != null)
+      progressBar.registerHandler(id, handler);
   }
 
   /**
@@ -1000,7 +1001,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public boolean operationInProgress()
   {
-    return progressBar.operationInProgress();
+    return progressBar == null ? false : progressBar.operationInProgress();
   }
 
   /**
index 806bfc1..b7bac37 100644 (file)
@@ -452,10 +452,12 @@ public class AppJmol extends StructureViewerBase
                 RenderingHints.VALUE_ANTIALIAS_ON);
         if (type == TYPE.PNG && usescale > 0.0f)
         {
-          // for a scaled image, this scales down a bigger image to increase
-          // resolution
-
-          ig2.scale(1 / usescale, 1 / usescale);
+          // for a scaled image, this scales down a bigger image to give the
+          // right resolution
+          if (usescale > 0.0f)
+          {
+            ig2.scale(1 / usescale, 1 / usescale);
+          }
         }
 
         jmb.jmolViewer.antialiased = true;
index 790873f..2b16495 100644 (file)
@@ -1669,20 +1669,21 @@ public class StructureChooser extends GStructureChooser
   @Override
   public void setProgressBar(String message, long id)
   {
-    if (!Platform.isHeadless())
+    if (!Platform.isHeadless() && progressBar != null)
       progressBar.setProgressBar(message, id);
   }
 
   @Override
   public void registerHandler(long id, IProgressIndicatorHandler handler)
   {
-    progressBar.registerHandler(id, handler);
+    if (progressBar != null)
+      progressBar.registerHandler(id, handler);
   }
 
   @Override
   public boolean operationInProgress()
   {
-    return progressBar.operationInProgress();
+    return progressBar == null ? false : progressBar.operationInProgress();
   }
 
   public JalviewStructureDisplayI getOpenedStructureViewer()
index 20ccd11..7ed1ea3 100644 (file)
@@ -105,6 +105,34 @@ public class CommandsTest
             lookForSequenceName("THIS_SEQUENCE_ID_DOESN'T_EXIST"));
   }
 
+  @Test(groups = "Functional", dataProvider = "structureImageOutputFiles")
+  public void structureImageOutputTest(String cmdLine, String[] filenames)
+          throws IOException
+  {
+    cleanupFiles(filenames);
+    String[] args = cmdLine.split("\\s+");
+    Jalview.main(args);
+    Commands cmds = Jalview.getInstance().getCommands();
+    Assert.assertNotNull(cmds);
+    File lastFile = null;
+    for (String filename : filenames)
+    {
+      File file = new File(filename);
+      Assert.assertTrue(file.exists(), "File '" + filename
+              + "' was not created by '" + cmdLine + "'");
+      Assert.assertTrue(file.isFile(), "File '" + filename
+              + "' is not a file from '" + cmdLine + "'");
+      Assert.assertTrue(Files.size(file.toPath()) > 0, "File '" + filename
+              + "' has no content from '" + cmdLine + "'");
+      // make sure the successive output files get bigger!
+      if (lastFile != null)
+        Assert.assertTrue(
+                Files.size(file.toPath()) > Files.size(lastFile.toPath()));
+    }
+    cleanupFiles(filenames);
+    tearDown();
+  }
+
   @Test(groups = "Functional", dataProvider = "argfileOutputFiles")
   public void argFilesGlobAndSubstitutionsTest(String cmdLine,
           String[] filenames) throws IOException
@@ -133,6 +161,48 @@ public class CommandsTest
     tearDown();
   }
 
+  @DataProvider(name = "structureImageOutputFiles")
+  public Object[][] structureImageOutputFiles()
+  {
+    return new Object[][] {
+        //
+        { "--nonews --nosplash --open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles + "/structureimage1.png "
+                + "--open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles
+                + "/structureimage2.png --structureimagescale=1.5"
+                + "--open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles
+                + "/structureimage3.png --structureimagescale=2.0",
+            new String[]
+            { testfiles + "/structureimage1.png",
+                testfiles + "/structureimage2.png",
+                testfiles + "/structureimage3.png" } },
+        /*
+        { "--headless --noquit --open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles + "/structureimage1.png "
+                + "--open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles
+                + "/structureimage2.png --structureimagescale=1.5"
+                + "--open=./examples/test_fab41.result/sample.a2m "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureimage=" + testfiles
+                + "/structureimage3.png --structureimagescale=2.0",
+            new String[]
+            { testfiles + "/structureimage1.png",
+                testfiles + "/structureimage2.png",
+                testfiles + "/structureimage3.png" } },
+                */
+        //
+    };
+
+  }
+
   @DataProvider(name = "argfileOutputFiles")
   public Object[][] argfileOutputFiles()
   {
index bd63232..a31b77d 100644 (file)
@@ -131,8 +131,7 @@ public class CommandsTest2
       String cmdLine,
       int seqNum,
       int annNum,
-      int viewerNum,
-      String propsFile
+      int structureViewerNum,
      */
     return new Object[][] {
         //
@@ -182,6 +181,20 @@ public class CommandsTest2
             16, 19, 3 },
         { "--nonews --nosplash --debug --nowebservicediscovery --props=test/jalview/bin/commandsTest.jvprops --argfile=test/jalview/bin/commandsTest2.argfile2 ",
             16, 0, 2 },
+        { "--nonews --nosplash --debug --nowebservicediscovery --props=test/jalview/bin/commandsTest.jvprops --open=./examples/test_fab41.result/sample.a2m "
+                + "--allstructures "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--structureviewer=none "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4.pdb "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2.pdb",
+            16, 10, 0 },
+        { "--nonews --nosplash --debug --nowebservicediscovery --props=test/jalview/bin/commandsTest.jvprops --open=./examples/test_fab41.result/sample.a2m "
+                + "--allstructures "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb "
+                + "--noallstructures " + "--structureviewer=none "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4.pdb "
+                + "--structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2.pdb",
+            16, 10, 2 },
         /*
          */
         //
index bc2be78..ca63e44 100644 (file)
@@ -184,9 +184,12 @@ public class ArgParserTest
   @DataProvider(name = "argSubValsAndLinkedIds")
   public Object[][] argSubValsAndLinkedIds()
   {
-    return new Object[][] { {
-        "--debug --append=[hi]test/jalview/bin/argparser/testfiles/test1.fa",
-        "JALVIEW:0", Arg.APPEND, "hi", "true", true },
+    return new Object[][] {
+        //
+        /*
+         */
+        { "--debug --append=[hi]test/jalview/bin/argparser/testfiles/test1.fa",
+            "JALVIEW:0", Arg.APPEND, "hi", "true", true },
         { "--append[linkedId1]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
             "linkedId1", Arg.APPEND, "new", "true", true },
         { "--append[linkedId2]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
@@ -198,7 +201,11 @@ public class ArgParserTest
         { "--append[linkedId5]=[new,hello=worlddomination,1]test/jalview/bin/argparser/testfiles/test1.fa --append[linkedId2]=[new;hello=world;1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
             "linkedId5", Arg.APPEND, "hello", "world", false },
         { "--append[linkedId6]=[new,hello=world,0]test/jalview/bin/argparser/testfiles/test1.fa --append[linkedId7]=[new;hello=world;1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
-            "linkedId7", Arg.APPEND, "GETINDEX", "0", false }, };
+            "linkedId7", Arg.APPEND, "GETINDEX", "0", false },
+        /*
+         */
+        //
+    };
   }
 
   @DataProvider(name = "argAutoIndexAndSubstitutions")