JAL-4269 Now using --width, --height, --scale for both --image and --structureimage...
authorBen Soares <b.soares@dundee.ac.uk>
Sat, 16 Sep 2023 11:24:31 +0000 (12:24 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Sat, 16 Sep 2023 11:24:31 +0000 (12:24 +0100)
src/jalview/bin/Commands.java
src/jalview/bin/argparser/Arg.java
src/jalview/bin/argparser/ArgParser.java
src/jalview/bin/argparser/ArgValuesMap.java
test/jalview/bin/CommandsTest.java

index ba72831..3173b35 100644 (file)
@@ -17,7 +17,6 @@ import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Jalview.ExitCode;
 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.ArgValuesMap;
 import jalview.bin.argparser.SubVals;
@@ -55,6 +54,7 @@ import jalview.util.ImageMaker;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.StringUtils;
 import jalview.util.imagemaker.BitmapImageSizing;
 
 public class Commands
@@ -290,29 +290,14 @@ public class Commands
             }
           }
 
-          // colour alignment?
-          String colour = ArgParser.getFromSubValArgOrPref(avm, av,
-                  Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", "");
-          if ("" != colour)
-          {
-            ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
-                    af.getViewport(), af.getViewport().getAlignment(),
-                    colour);
-
-            if (cs == null && !"None".equals(colour))
-            {
-              addWarn("Couldn't parse '" + colour + "' as a colourscheme.");
-            }
-            else
-            {
-              af.changeColour(cs);
-            }
-            Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
-          }
+          // colour alignment
+          String colour = avm.getFromSubValArgOrPref(av, Arg.COLOUR, sv,
+                  null, "DEFAULT_COLOUR_PROT", "");
+          this.colourAlignFrame(af, colour);
 
           // Change alignment frame title
-          String title = ArgParser.getFromSubValArgOrPref(avm, av,
-                  Arg.TITLE, sv, null, null, null);
+          String title = avm.getFromSubValArgOrPref(av, Arg.TITLE, sv, null,
+                  null, null);
           if (title != null)
           {
             af.setTitle(title);
@@ -320,7 +305,7 @@ public class Commands
           }
 
           // Add features
-          String featuresfile = ArgParser.getValueFromSubValOrArg(avm, av,
+          String featuresfile = avm.getValueFromSubValOrArg(av,
                   Arg.FEATURES, sv);
           if (featuresfile != null)
           {
@@ -331,8 +316,8 @@ public class Commands
           }
 
           // Add annotations from file
-          String annotationsfile = ArgParser.getValueFromSubValOrArg(avm,
-                  av, Arg.ANNOTATIONS, sv);
+          String annotationsfile = avm.getValueFromSubValOrArg(av,
+                  Arg.ANNOTATIONS, sv);
           if (annotationsfile != null)
           {
             af.loadJalviewDataFile(annotationsfile, null, null, null);
@@ -342,8 +327,8 @@ public class Commands
           }
 
           // Set or clear the sortbytree flag
-          boolean sortbytree = ArgParser.getBoolFromSubValOrArg(avm,
-                  Arg.SORTBYTREE, sv);
+          boolean sortbytree = avm.getBoolFromSubValOrArg(Arg.SORTBYTREE,
+                  sv);
           if (sortbytree)
           {
             af.getViewport().setSortByTree(true);
@@ -351,8 +336,7 @@ public class Commands
           }
 
           // Load tree from file
-          String treefile = ArgParser.getValueFromSubValOrArg(avm, av,
-                  Arg.TREE, sv);
+          String treefile = avm.getValueFromSubValOrArg(av, Arg.TREE, sv);
           if (treefile != null)
           {
             try
@@ -371,13 +355,13 @@ public class Commands
           }
 
           // Show secondary structure annotations?
-          boolean showSSAnnotations = ArgParser.getFromSubValArgOrPref(avm,
+          boolean showSSAnnotations = avm.getFromSubValArgOrPref(
                   Arg.SHOWSSANNOTATIONS, av.getSubVals(), null,
                   "STRUCT_FROM_PDB", true);
           af.setAnnotationsVisibility(showSSAnnotations, true, false);
 
           // Show sequence annotations?
-          boolean showAnnotations = ArgParser.getFromSubValArgOrPref(avm,
+          boolean showAnnotations = avm.getFromSubValArgOrPref(
                   Arg.SHOWANNOTATIONS, av.getSubVals(), null,
                   "SHOW_ANNOTATIONS", true);
           af.setAnnotationsVisibility(showAnnotations, false, true);
@@ -395,7 +379,7 @@ public class Commands
           }
 
           // wrap alignment? do this last for formatting reasons
-          wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv, null,
+          wrap = avm.getFromSubValArgOrPref(Arg.WRAP, sv, null,
                   "WRAP_ALIGNMENT", false);
           // af.setWrapFormat(wrap) is applied after structures are opened for
           // annotation reasons
@@ -456,6 +440,7 @@ public class Commands
         {
           String val = av.getValue();
           SubVals subVals = av.getSubVals();
+          int argIndex = av.getArgIndex();
           SequenceI seq = getSpecifiedSequence(af, avm, av);
           if (seq == null)
           {
@@ -526,10 +511,9 @@ public class Commands
           String structureFilepath = structureFile.getAbsolutePath();
 
           // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
-          String paeFilepath = ArgParser
-                  .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
-                          Arg.PAEMATRIX, Position.AFTER, av, subVals, null,
-                          null, null);
+          String paeFilepath = avm.getFromSubValArgOrPrefWithSubstitutions(
+                  argParser, Arg.PAEMATRIX, ArgValuesMap.Position.AFTER, av,
+                  subVals, null, null, null);
           if (paeFilepath != null)
           {
             File paeFile = new File(paeFilepath);
@@ -546,19 +530,17 @@ public class Commands
           }
 
           // showing annotations from structure file or not
-          boolean ssFromStructure = ArgParser.getFromSubValArgOrPref(avm,
+          boolean ssFromStructure = avm.getFromSubValArgOrPref(
                   Arg.SHOWSSANNOTATIONS, subVals, null, "STRUCT_FROM_PDB",
                   true);
 
           // get TEMPFAC type from subvals or Arg.TEMPFAC in case user Adds
           // reference annotations
-          String tftString = ArgParser
-                  .getFromSubValArgOrPrefWithSubstitutions(argParser, avm,
-                          Arg.TEMPFAC, Position.AFTER, av, subVals, null,
-                          null, null);
-          boolean notempfac = ArgParser.getFromSubValArgOrPref(avm,
-                  Arg.NOTEMPFAC, subVals, null, "ADD_TEMPFACT_ANN", false,
-                  true);
+          String tftString = avm.getFromSubValArgOrPrefWithSubstitutions(
+                  argParser, Arg.TEMPFAC, ArgValuesMap.Position.AFTER, av,
+                  subVals, null, null, null);
+          boolean notempfac = avm.getFromSubValArgOrPref(Arg.NOTEMPFAC,
+                  subVals, null, "ADD_TEMPFACT_ANN", false, true);
           TFType tft = notempfac ? null : TFType.DEFAULT;
           if (tftString != null && !notempfac)
           {
@@ -587,18 +569,18 @@ public class Commands
             }
           }
 
-          String sViewer = ArgParser.getFromSubValArgOrPref(avm,
-                  Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, null,
-                  null, "jmol");
-          ViewerType viewerType = ViewerType.getFromString(sViewer);
+          String sViewerName = avm.getFromSubValArgOrPref(
+                  Arg.STRUCTUREVIEWER, ArgValuesMap.Position.AFTER, av,
+                  subVals, null, null, "jmol");
+          ViewerType viewerType = ViewerType.getFromString(sViewerName);
 
           // TODO use ssFromStructure
-          StructureViewer sv = StructureChooser
+          StructureViewer structureViewer = StructureChooser
                   .openStructureFileForSequence(null, null, ap, seq, false,
                           structureFilepath, tft, paeFilepath, false,
                           ssFromStructure, false, viewerType);
 
-          if (sv == null)
+          if (structureViewer == null)
           {
             addError("Failed to import and open structure view for file '"
                     + structureFile + "'.");
@@ -607,17 +589,17 @@ public class Commands
           try
           {
             long tries = 1000;
-            while (sv.isBusy() && tries > 0)
+            while (structureViewer.isBusy() && tries > 0)
             {
               Thread.sleep(25);
-              if (sv.isBusy())
+              if (structureViewer.isBusy())
               {
                 tries--;
                 Console.debug(
                         "Waiting for viewer for " + structureFilepath);
               }
             }
-            if (tries == 0 && sv.isBusy())
+            if (tries == 0 && structureViewer.isBusy())
             {
               addWarn("Gave up waiting for structure viewer to load file '"
                       + structureFile
@@ -639,88 +621,106 @@ public class Commands
           {
             svMap.put(id, new ArrayList<>());
           }
-          svMap.get(id).add(sv);
+          svMap.get(id).add(structureViewer);
 
           Console.debug(
                   "Successfully opened viewer for " + structureFilepath);
-          String structureImageFilename = ArgParser.getValueFromSubValOrArg(
-                  avm, av, Arg.STRUCTUREIMAGE, subVals);
-          if (sv != null && structureImageFilename != null)
+
+          if (avm.containsArg(Arg.STRUCTUREIMAGE))
           {
-            ArgValue siAv = avm.getClosestNextArgValueOfArg(av,
-                    Arg.STRUCTUREIMAGE);
-            SubVals sisv = null;
-            if (structureImageFilename.equals(siAv.getValue()))
-            {
-              sisv = siAv.getSubVals();
-            }
-            File structureImageFile = new File(structureImageFilename);
-            String width = ArgParser.getValueFromSubValOrArg(avm, av,
-                    Arg.STRUCTUREIMAGEWIDTH, sisv);
-            String height = ArgParser.getValueFromSubValOrArg(avm, av,
-                    Arg.STRUCTUREIMAGEHEIGHT, sisv);
-            String scale = ArgParser.getValueFromSubValOrArg(avm, av,
-                    Arg.STRUCTUREIMAGESCALE, sisv);
-            String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
-                    Arg.STRUCTUREIMAGETEXTRENDERER, sisv);
-            String typeS = ArgParser.getValueFromSubValOrArg(avm, av,
-                    Arg.STRUCTUREIMAGETYPE, sisv);
-            if (typeS == null || typeS.length() == 0)
-            {
-              typeS = FileUtils.getExtension(structureImageFile);
-            }
-            TYPE imageType;
-            try
-            {
-              imageType = Enum.valueOf(TYPE.class,
-                      typeS.toUpperCase(Locale.ROOT));
-            } catch (IllegalArgumentException e)
-            {
-              addWarn("Do not know image format '" + typeS
-                      + "', using PNG");
-              imageType = TYPE.PNG;
-            }
-            BitmapImageSizing userBis = ImageMaker
-                    .parseScaleWidthHeightStrings(scale, width, height);
-            // TODO MAKE THIS VIEWER INDEPENDENT!!
-            switch (StructureViewer.getViewerType())
+            for (ArgValue structureImageArgValue : avm
+                    .getArgValueList(Arg.STRUCTUREIMAGE))
             {
-            case JMOL:
-              JalviewStructureDisplayI sview = sv
-                      .getJalviewStructureDisplay();
-              if (sview instanceof AppJmol)
+              String structureImageFilename = structureImageArgValue
+                      .getValue();
+              if (structureViewer != null && structureImageFilename != null)
               {
-                AppJmol jmol = (AppJmol) sview;
+                SubVals structureImageSubVals = null;
+                structureImageSubVals = structureImageArgValue.getSubVals();
+                File structureImageFile = new File(structureImageFilename);
+                String width = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.WIDTH,
+                        structureImageSubVals);
+                String height = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.HEIGHT,
+                        structureImageSubVals);
+                String scale = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.SCALE,
+                        structureImageSubVals);
+                String renderer = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.TEXTRENDERER,
+                        structureImageSubVals);
+                String typeS = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.TYPE,
+                        structureImageSubVals);
+                if (typeS == null || typeS.length() == 0)
+                {
+                  typeS = FileUtils.getExtension(structureImageFile);
+                }
+                TYPE imageType;
                 try
                 {
-                  boolean success = this.checksBeforeWritingToFile(avm,
-                          subVals, false, structureImageFilename,
-                          "structure image", isError);
-                  if (!success)
+                  imageType = Enum.valueOf(TYPE.class,
+                          typeS.toUpperCase(Locale.ROOT));
+                } catch (IllegalArgumentException e)
+                {
+                  addWarn("Do not know image format '" + typeS
+                          + "', using PNG");
+                  imageType = TYPE.PNG;
+                }
+                BitmapImageSizing userBis = ImageMaker
+                        .parseScaleWidthHeightStrings(scale, width, height);
+
+                String imageColour = avm.getValueFromSubValOrArg(
+                        structureImageArgValue, Arg.IMAGECOLOUR,
+                        structureImageSubVals);
+                ColourSchemeI originalColourScheme = this
+                        .getColourScheme(af);
+                this.colourAlignFrame(af, imageColour);
+
+                // TODO MAKE THIS VIEWER INDEPENDENT!!
+                switch (StructureViewer.getViewerType())
+                {
+                case JMOL:
+                  JalviewStructureDisplayI sview = structureViewer
+                          .getJalviewStructureDisplay();
+                  if (sview instanceof AppJmol)
                   {
-                    continue;
-                  }
+                    AppJmol jmol = (AppJmol) sview;
+                    try
+                    {
+                      boolean success = this.checksBeforeWritingToFile(avm,
+                              subVals, false, structureImageFilename,
+                              "structure image", isError);
+                      if (!success)
+                      {
+                        continue;
+                      }
+
+                      Console.debug(
+                              "Rendering image to " + structureImageFile);
+                      jmol.makePDBImage(structureImageFile, imageType,
+                              renderer, userBis);
+                      Console.debug("Finished Rendering image to "
+                              + structureImageFile);
+
+                    } catch (ImageOutputException ioexc)
+                    {
+                      addError("Unexpected error whilst exporting image to "
+                              + structureImageFile, ioexc);
+                      isError = true;
+                      continue;
+                    }
 
-                  Console.debug("Rendering image to " + structureImageFile);
-                  jmol.makePDBImage(structureImageFile, imageType, renderer,
-                          userBis);
-                  Console.debug("Finished Rendering image to "
-                          + structureImageFile);
-
-                } catch (ImageOutputException ioexc)
-                {
-                  addError("Unexpected error whilst exporting image to "
-                          + structureImageFile, ioexc);
-                  isError = true;
+                  }
+                  break;
+                default:
+                  addWarn("Cannot export image for structure viewer "
+                          + structureViewer.getViewerType() + " yet");
                   continue;
                 }
-
+                this.colourAlignFrame(af, originalColourScheme);
               }
-              break;
-            default:
-              addWarn("Cannot export image for structure viewer "
-                      + sv.getViewerType() + " yet");
-              continue;
             }
           }
         }
@@ -794,29 +794,29 @@ public class Commands
     Boolean isError = Boolean.valueOf(false);
     if (avm.containsArg(Arg.IMAGE))
     {
-      for (ArgValue av : avm.getArgValueList(Arg.IMAGE))
+      for (ArgValue imageAv : avm.getArgValueList(Arg.IMAGE))
       {
-        String val = av.getValue();
-        SubVals subVal = av.getSubVals();
-        String fileName = subVal.getContent();
+        String val = imageAv.getValue();
+        SubVals imageSubVals = imageAv.getSubVals();
+        String fileName = imageSubVals.getContent();
         File file = new File(fileName);
         String name = af.getName();
-        String renderer = ArgParser.getValueFromSubValOrArg(avm, av,
-                Arg.TEXTRENDERER, subVal);
+        String renderer = avm.getValueFromSubValOrArg(imageAv,
+                Arg.TEXTRENDERER, imageSubVals);
         if (renderer == null)
           renderer = "text";
         String type = "png"; // default
 
-        String scale = ArgParser.getValueFromSubValOrArg(avm, av, Arg.SCALE,
-                subVal);
-        String width = ArgParser.getValueFromSubValOrArg(avm, av, Arg.WIDTH,
-                subVal);
-        String height = ArgParser.getValueFromSubValOrArg(avm, av,
-                Arg.HEIGHT, subVal);
+        String scale = avm.getValueFromSubValOrArg(imageAv, Arg.SCALE,
+                imageSubVals);
+        String width = avm.getValueFromSubValOrArg(imageAv, Arg.WIDTH,
+                imageSubVals);
+        String height = avm.getValueFromSubValOrArg(imageAv, Arg.HEIGHT,
+                imageSubVals);
         BitmapImageSizing userBis = ImageMaker
                 .parseScaleWidthHeightStrings(scale, width, height);
 
-        type = ArgParser.getValueFromSubValOrArg(avm, av, Arg.TYPE, subVal);
+        type = avm.getValueFromSubValOrArg(imageAv, Arg.TYPE, imageSubVals);
         if (type == null && fileName != null)
         {
           for (String ext : new String[] { "svg", "png", "html", "eps" })
@@ -831,10 +831,15 @@ public class Commands
         Cache.setPropsAreReadOnly(true);
         Cache.setProperty("EXPORT_EMBBED_BIOJSON", "false");
 
+        String imageColour = avm.getValueFromSubValOrArg(imageAv,
+                Arg.IMAGECOLOUR, imageSubVals);
+        ColourSchemeI originalColourScheme = this.getColourScheme(af);
+        this.colourAlignFrame(af, imageColour);
+
         Console.info("Writing " + file);
 
-        boolean success = checksBeforeWritingToFile(avm, subVal, false,
-                fileName, "image", isError);
+        boolean success = checksBeforeWritingToFile(avm, imageSubVals,
+                false, fileName, "image", isError);
         if (!success)
         {
           continue;
@@ -897,6 +902,8 @@ public class Commands
                   ioex);
           isError = true;
         }
+
+        this.colourAlignFrame(af, originalColourScheme);
       }
     }
     return !isError;
@@ -926,8 +933,8 @@ public class Commands
         File file = new File(fileName);
 
         String name = af.getName();
-        String format = ArgParser.getValueFromSubValOrArg(avm, av,
-                Arg.FORMAT, subVals);
+        String format = avm.getValueFromSubValOrArg(av, Arg.FORMAT,
+                subVals);
         FileFormats ffs = FileFormats.getInstance();
         List<String> validFormats = ffs.getWritableFormats(false);
 
@@ -987,9 +994,8 @@ public class Commands
           continue;
         }
 
-        boolean backups = ArgParser.getFromSubValArgOrPref(avm, Arg.BACKUPS,
-                subVals, null,
-                Platform.isHeadless() ? null : BackupFiles.ENABLED,
+        boolean backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVals,
+                null, Platform.isHeadless() ? null : BackupFiles.ENABLED,
                 !Platform.isHeadless());
 
         Console.info("Writing " + fileName);
@@ -1017,7 +1023,7 @@ public class Commands
           ArgValue av)
   {
     SubVals subVals = av.getSubVals();
-    ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID);
+    ArgValue idAv = avm.getClosestNextArgValueOfArg(av, Arg.SEQID, true);
     SequenceI seq = null;
     if (subVals == null && idAv == null)
       return null;
@@ -1077,6 +1083,36 @@ public class Commands
     return svs;
   }
 
+  private void colourAlignFrame(AlignFrame af, String colour)
+  {
+    // use string "none" to remove colour scheme
+    if (colour != null && "" != colour)
+    {
+      ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+              af.getViewport(), af.getViewport().getAlignment(), colour);
+      if (cs == null && !StringUtils.equalsIgnoreCase(colour, "none"))
+      {
+        addWarn("Couldn't parse '" + colour + "' as a colourscheme.");
+      }
+      else
+      {
+        Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour);
+        colourAlignFrame(af, cs);
+      }
+    }
+  }
+
+  private void colourAlignFrame(AlignFrame af, ColourSchemeI cs)
+  {
+    // Note that cs == null removes colour scheme from af
+    af.changeColour(cs);
+  }
+
+  private ColourSchemeI getColourScheme(AlignFrame af)
+  {
+    return af.getViewport().getGlobalColourScheme();
+  }
+
   private void addInfo(String errorMessage)
   {
     Console.info(errorMessage);
@@ -1106,8 +1142,8 @@ public class Commands
   {
     File file = new File(filename);
 
-    boolean overwrite = ArgParser.getFromSubValArgOrPref(avm, Arg.OVERWRITE,
-            subVal, null, "OVERWRITE_OUTPUT", false);
+    boolean overwrite = avm.getFromSubValArgOrPref(Arg.OVERWRITE, subVal,
+            null, "OVERWRITE_OUTPUT", false);
     boolean stdout = false;
     boolean backups = false;
     if (includeBackups)
@@ -1116,8 +1152,8 @@ public class Commands
       // backups. Use the Arg.BACKUPS or subval "backups" setting first,
       // otherwise if headless assume false, if not headless use the user
       // preference with default true.
-      backups = ArgParser.getFromSubValArgOrPref(avm, Arg.BACKUPS, subVal,
-              null, Platform.isHeadless() ? null : BackupFiles.ENABLED,
+      backups = avm.getFromSubValArgOrPref(Arg.BACKUPS, subVal, null,
+              Platform.isHeadless() ? null : BackupFiles.ENABLED,
               !Platform.isHeadless());
     }
 
@@ -1130,8 +1166,8 @@ public class Commands
       return false;
     }
 
-    boolean mkdirs = ArgParser.getFromSubValArgOrPref(avm, Arg.MKDIRS,
-            subVal, null, "MKDIRS_OUTPUT", false);
+    boolean mkdirs = avm.getFromSubValArgOrPref(Arg.MKDIRS, subVal, null,
+            "MKDIRS_OUTPUT", false);
 
     if (!FileUtils.checkParentDir(file, mkdirs))
     {
index 9dd5c93..51a2d16 100644 (file)
@@ -157,45 +157,56 @@ public enum Arg
   IMAGE(Type.IMAGE,
           "Output an image of the open alignment window. Format is specified by the subval modifier, a following --type argument or guessed from the file extension. Valid formats/extensions are:\n"
                   + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
-          Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWMULTIID,
-          Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
+          Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.MULTIVALUE,
+          Opt.ALLOWMULTIID, Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
+  STRUCTUREIMAGE(Type.IMAGE,
+          "Export an image of a 3D structure opened in JMOL", Opt.STRING,
+          Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID,
+          Opt.PRIMARY),
   TYPE(Type.IMAGE,
-          "Set the image format for the preceding --image. Valid values are:\n"
-                  + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
-          Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
+          "Set the image format for the preceding " + Arg.IMAGE.argString()
+                  + " or " + Arg.STRUCTUREIMAGE.argString()
+                  + ". Valid values are:\n" + "svg,\n" + "png,\n" + "eps,\n"
+                  + "html,\n" + "biojs.",
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
   TEXTRENDERER(Type.IMAGE,
           "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
                   + "text,\n" + "lineart.",
-          Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
   SCALE(Type.IMAGE,
           "Sets a scaling for bitmap image format (PNG). Should be given as a floating point number. If used in conjunction with --width and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
-          Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
   WIDTH(Type.IMAGE,
           "Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
-          Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
   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.ALLOWMULTIID),
-  STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
-          "Export an image of a 3D structure opened in JMOL", Opt.STRING,
-          Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID),
-  STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
+  IMAGECOLOUR(Type.IMAGE, "imagecolor", // being a bit soft on the Americans!
+          "Applies the colour scheme to the open alignment window for this image, otherwise the value of "
+                  + Arg.COLOUR.argString()
+                  + " (or none) will apply. Valid values are the same as "
+                  + Arg.COLOUR.argString() + ".",
+          Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
+  /*
+  STRUCTUREIMAGETYPE(Type.IMAGE,
           "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.ALLOWMULTIID),
-  STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
+  STRUCTUREIMAGETEXTRENDERER(Type.IMAGE,
           "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.ALLOWMULTIID),
-  STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
+  STRUCTUREIMAGESCALE(Type.IMAGE,
           "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.ALLOWMULTIID),
-  STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
+  STRUCTUREIMAGEWIDTH(Type.IMAGE,
           "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.ALLOWMULTIID),
-  STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
+  STRUCTUREIMAGEHEIGHT(Type.IMAGE,
           "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.ALLOWMULTIID),
+  */
 
   OUTPUT(Type.OUTPUT,
           "Export the open alignment to file filename. The format name is specified by the subval modifier format=name, a following --format name argument or guessed from the file extension. Valid format names (and file extensions) are:\n"
@@ -463,8 +474,9 @@ public enum Arg
     STRUCTURE("arguments used to add and format 3D structure data"),
     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"),
+    IMAGE("arguments used to export an image of an alignment or structure"),
+    // 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 7581dde..fd42bb2 100644 (file)
@@ -31,10 +31,8 @@ import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
-import jalview.bin.Cache;
 import jalview.bin.Console;
 import jalview.bin.Jalview;
 import jalview.bin.Jalview.ExitCode;
@@ -619,10 +617,9 @@ public class ArgParser
 
         // 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 (allStructures && (a.getType() == Type.STRUCTURE
+        // || a.getType() == Type.STRUCTUREIMAGE)
+        ) && !a.hasOption(Opt.PRIMARY))
         {
           if (!subvals.has(Arg.ALLSTRUCTURES.getName()))
           // && !subvals.has("structureid"))
@@ -971,161 +968,6 @@ public class ArgParser
     return args;
   }
 
-  public static enum Position
-  {
-    FIRST, BEFORE, AFTER
-  }
-
-  /**
-   * 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
-   */
-  public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
-          Arg a, SubVals sv, String key, String pref, String def)
-  {
-    return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
-            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)
-  {
-    return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
-            sv, key, pref, def);
-  }
-
-  public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
-          ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
-          String key, String pref, String def)
-  {
-    if (key == null)
-      key = a.getName();
-    String value = null;
-    if (sv != null && sv.has(key) && sv.get(key) != null)
-    {
-      value = ap == null ? sv.get(key)
-              : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
-    }
-    else if (avm != null && avm.containsArg(a))
-    {
-      if (pos == Position.FIRST && avm.getValue(a) != null)
-        value = avm.getValue(a);
-      else if (pos == Position.BEFORE
-              && avm.getClosestPreviousArgValueOfArg(av, a) != null)
-        value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
-      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();
-        }
-      }
-    }
-    if (value == null)
-    {
-      value = pref != null ? Cache.getDefault(pref, def) : def;
-    }
-    return value;
-  }
-
-  public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
-          SubVals sv)
-  {
-    return getFromSubValArgOrPref(avm, a, sv, null, null, false);
-  }
-
-  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;
-
-    boolean usingArgKey = false;
-    if (key == null)
-    {
-      key = a.getName();
-      usingArgKey = true;
-    }
-
-    String nokey = ArgParser.NEGATESTRING + key;
-
-    // look for key or nokey in subvals first (if using Arg check options)
-    if (sv != null)
-    {
-      // check for true boolean
-      if (sv.has(key) && sv.get(key) != null)
-      {
-        if (usingArgKey)
-        {
-          if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
-          {
-            Console.debug(
-                    "Looking for boolean in subval from non-boolean/non-unary Arg "
-                            + a.getName());
-            return false;
-          }
-        }
-        return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
-      }
-
-      // check for negative boolean (subval "no..." will be "true")
-      if (sv.has(nokey) && sv.get(nokey) != null)
-      {
-        if (usingArgKey)
-        {
-          if (!(a.hasOption(Opt.BOOLEAN)))
-          {
-            Console.debug(
-                    "Looking for negative boolean in subval from non-boolean Arg "
-                            + a.getName());
-            return false;
-          }
-        }
-        return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
-      }
-    }
-
-    // check argvalues
-    if (avm != null && avm.containsArg(a))
-      return avm.getBoolean(a);
-
-    // return preference or default
-    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
   // linkedId ArgValues if it does.
   /**
@@ -1138,13 +980,6 @@ public class ArgParser
             argIndex, doSubs);
   }
 
-  private void addValue(String linkedId, Type type, ArgValues avs, String v,
-          int argIndex, boolean doSubs)
-  {
-    this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, null, v, false,
-            argIndex, doSubs);
-  }
-
   private void setBoolean(String linkedId, Type type, ArgValues avs,
           boolean b, int argIndex)
   {
@@ -1367,4 +1202,5 @@ public class ArgParser
   {
     return mixedExamples;
   }
+
 }
\ No newline at end of file
index 4aa8570..99a4836 100644 (file)
@@ -2,12 +2,17 @@ package jalview.bin.argparser;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
+import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.bin.argparser.Arg.Opt;
+import jalview.bin.argparser.Arg.Type;
 import jalview.util.FileUtils;
 
 /**
@@ -16,6 +21,8 @@ import jalview.util.FileUtils;
  */
 public class ArgValuesMap
 {
+  private List<ArgInfo> argInfoList = new ArrayList<>();
+
   protected Map<Arg, ArgValues> m;
 
   private String linkedId;
@@ -165,7 +172,8 @@ public class ArgValuesMap
     return closestAv;
   }
 
-  public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a)
+  public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a,
+          boolean withinType)
   {
     // this looks for the *next* arg that *might* be referring back to
     // a thisAv. Such an arg would have no subValues (if it does it should
@@ -185,6 +193,35 @@ public class ArgValuesMap
         closestAv = av;
       }
     }
+
+    // check if withinType this closestAv doesn't belong to the next primary arg
+    // of this type
+    if (withinType && closestAv != null)
+    {
+      int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE;
+      for (Arg tmpA : this.getArgKeys())
+      {
+        // interested in Opt.PRIMARY args of the same type
+        if (tmpA.getType() == a.getType() && tmpA.hasOption(Opt.PRIMARY))
+        {
+          for (ArgValue tmpAv : getArgValueList(tmpA))
+          {
+            int tmpArgIndex = tmpAv.getArgIndex();
+            if (tmpArgIndex > thisArgIndex
+                    && tmpArgIndex < nextPrimaryArgOfSameTypeIndex)
+            {
+              nextPrimaryArgOfSameTypeIndex = tmpArgIndex;
+            }
+          }
+        }
+      }
+      if (nextPrimaryArgOfSameTypeIndex < closestAv.getArgIndex())
+      {
+        // looks licke closestAv actually belongs to a different primary Arg
+        return null;
+      }
+    }
+
     return closestAv;
   }
 
@@ -281,4 +318,226 @@ public class ArgValuesMap
     }
     return false;
   }
+
+  /*
+   * ArgInfo is a more straightforward list of arguments and their info
+   */
+
+  public void addArgInfo(Arg arg, String value, SubVals subVals,
+          int argIndex)
+  {
+    argInfoList.add(new ArgInfo(arg, value, subVals, argIndex));
+  }
+
+  public List<ArgInfo> getArgInfoList()
+  {
+    Collections.sort(argInfoList);
+    return argInfoList;
+  }
+
+  /**
+   * get from following Arg of type a or subval of same name (lowercase)
+   */
+  public String getValueFromSubValOrArg(ArgValue av, Arg a, SubVals sv)
+  {
+    return getFromSubValArgOrPref(av, a, sv, null, null, null);
+  }
+
+  /**
+   * get from following Arg of type a or subval key or preference pref or
+   * default def
+   */
+  public String getFromSubValArgOrPref(ArgValue av, Arg a, SubVals sv,
+          String key, String pref, String def)
+  {
+    return getFromSubValArgOrPref(a, ArgValuesMap.Position.AFTER, av, sv,
+            key, pref, 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 String getFromSubValArgOrPref(Arg a, ArgValuesMap.Position pos,
+          ArgValue av, SubVals sv, String key, String pref, String def)
+  {
+    return getFromSubValArgOrPrefWithSubstitutions(null, a, pos, av, sv,
+            key, pref, def);
+  }
+
+  public String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap, Arg a,
+          ArgValuesMap.Position pos, ArgValue av, SubVals sv, String key,
+          String pref, String def)
+  {
+    return getFromSubValArgOrPrefWithSubstitutionsWithinType(ap, a, pos, av,
+            sv, key, pref, def, true);
+  }
+
+  public String getFromSubValArgOrPrefWithSubstitutionsWithinType(
+          ArgParser ap, Arg a, ArgValuesMap.Position pos, ArgValue av,
+          SubVals sv, String key, String pref, String def,
+          boolean withinType)
+  {
+    if (key == null)
+      key = a.getName();
+    String value = null;
+    if (sv != null && sv.has(key) && sv.get(key) != null)
+    {
+      value = ap == null ? sv.get(key)
+              : sv.getWithSubstitutions(ap, getLinkedId(), key);
+    }
+    else if (containsArg(a))
+    {
+      if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null)
+        value = getValue(a);
+      else if (pos == ArgValuesMap.Position.BEFORE
+              && getClosestPreviousArgValueOfArg(av, a) != null)
+        value = getClosestPreviousArgValueOfArg(av, a).getValue();
+      else if (pos == ArgValuesMap.Position.AFTER
+              && getClosestNextArgValueOfArg(av, a, withinType) != null)
+        value = getClosestNextArgValueOfArg(av, a, withinType).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 = getArgValueOfArgWithSubValKey(a,
+                Arg.ALLSTRUCTURES.getName());
+        if (av2 != null)
+        {
+          value = av2.getValue();
+        }
+      }
+    }
+    if (value == null)
+    {
+      value = pref != null ? Cache.getDefault(pref, def) : def;
+    }
+    return value;
+  }
+
+  public boolean getBoolFromSubValOrArg(Arg a, SubVals sv)
+  {
+    return getFromSubValArgOrPref(a, sv, null, null, false);
+  }
+
+  public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
+          String pref, boolean def)
+  {
+    return getFromSubValArgOrPref(a, sv, key, pref, def, false);
+  }
+
+  public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
+          String pref, boolean def, boolean invertPref)
+  {
+    if ((key == null && a == null) || (sv == null && a == null))
+      return false;
+
+    boolean usingArgKey = false;
+    if (key == null)
+    {
+      key = a.getName();
+      usingArgKey = true;
+    }
+
+    String nokey = ArgParser.NEGATESTRING + key;
+
+    // look for key or nokey in subvals first (if using Arg check options)
+    if (sv != null)
+    {
+      // check for true boolean
+      if (sv.has(key) && sv.get(key) != null)
+      {
+        if (usingArgKey)
+        {
+          if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
+          {
+            Console.debug(
+                    "Looking for boolean in subval from non-boolean/non-unary Arg "
+                            + a.getName());
+            return false;
+          }
+        }
+        return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
+      }
+
+      // check for negative boolean (subval "no..." will be "true")
+      if (sv.has(nokey) && sv.get(nokey) != null)
+      {
+        if (usingArgKey)
+        {
+          if (!(a.hasOption(Opt.BOOLEAN)))
+          {
+            Console.debug(
+                    "Looking for negative boolean in subval from non-boolean Arg "
+                            + a.getName());
+            return false;
+          }
+        }
+        return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
+      }
+    }
+
+    // check argvalues
+    if (containsArg(a))
+      return getBoolean(a);
+
+    // return preference or default
+    boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false;
+    return pref != null ? (invertPref ? !prefVal : prefVal) : def;
+  }
+
+  public class ArgInfo implements Comparable<ArgInfo>
+  {
+    private Arg arg;
+
+    private String value;
+
+    private SubVals subVals;
+
+    private int argIndex;
+
+    public ArgInfo(Arg arg, String value, SubVals subVals, int argIndex)
+    {
+      this.arg = arg;
+      this.value = value;
+      this.subVals = subVals;
+      this.argIndex = argIndex;
+    }
+
+    public Arg arg()
+    {
+      return arg;
+    }
+
+    public String value()
+    {
+      return value;
+    }
+
+    public SubVals subVals()
+    {
+      return subVals;
+    }
+
+    public int argIndex()
+    {
+      return argIndex;
+    }
+
+    @Override
+    public int compareTo(ArgInfo ai2)
+    {
+      return Integer.compare(this.argIndex(), ai2.argIndex());
+    }
+  }
+
+  public static enum Position
+  {
+    FIRST, BEFORE, AFTER
+  }
 }
index 66250d8..4d10c31 100644 (file)
@@ -214,11 +214,11 @@ public class CommandsTest
                 + "--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"
+                + "/structureimage2.png --scale=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",
+                + "/structureimage3.png --scale=2.0 ",
             new String[]
             { testfiles + "/structureimage1.png",
                 testfiles + "/structureimage2.png",
@@ -229,11 +229,11 @@ public class CommandsTest
                 + "--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"
+                + "/structureimage2.png --scale=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",
+                + "/structureimage3.png --scale=2.0 ",
             new String[]
             { testfiles + "/structureimage1.png",
                 testfiles + "/structureimage2.png",