JAL-4353 Add secondary Types for Args, for STRUCTUREIMAGE Type and restrict structure...
authorBen Soares <b.soares@dundee.ac.uk>
Tue, 5 Dec 2023 14:15:53 +0000 (14:15 +0000)
committerBen Soares <b.soares@dundee.ac.uk>
Tue, 5 Dec 2023 14:15:53 +0000 (14:15 +0000)
src/jalview/bin/Commands.java
src/jalview/bin/argparser/Arg.java
src/jalview/bin/argparser/ArgParser.java
src/jalview/bin/argparser/ArgValuesMap.java
src/jalview/bin/argparser/BootstrapArgs.java

index 475eba2..e01d40a 100644 (file)
@@ -501,15 +501,13 @@ public class Commands
       if (avm.containsArg(Arg.STRUCTURE))
       {
         commandArgsProvided = true;
-        for (
-
-        ArgValue av : avm.getArgValueList(Arg.STRUCTURE))
+        for (ArgValue structureAv : avm.getArgValueList(Arg.STRUCTURE))
         {
           argParser.setStructureFilename(null);
-          String val = av.getValue();
-          SubVals subVals = av.getSubVals();
-          int argIndex = av.getArgIndex();
-          SequenceI seq = getSpecifiedSequence(af, avm, av);
+          String val = structureAv.getValue();
+          SubVals subVals = structureAv.getSubVals();
+          int argIndex = structureAv.getArgIndex();
+          SequenceI seq = getSpecifiedSequence(af, avm, structureAv);
           if (seq == null)
           {
             // Could not find sequence from subId, let's assume the first
@@ -578,8 +576,8 @@ public class Commands
 
           // get PAEMATRIX file and label from subvals or Arg.PAEMATRIX
           String paeFilepath = avm.getFromSubValArgOrPrefWithSubstitutions(
-                  argParser, Arg.PAEMATRIX, ArgValuesMap.Position.AFTER, av,
-                  subVals, null, null, null);
+                  argParser, Arg.PAEMATRIX, ArgValuesMap.Position.AFTER,
+                  structureAv, subVals, null, null, null);
           if (paeFilepath != null)
           {
             File paeFile = new File(paeFilepath);
@@ -603,8 +601,8 @@ public class Commands
           // get TEMPFAC type from subvals or Arg.TEMPFAC in case user Adds
           // reference annotations
           String tftString = avm.getFromSubValArgOrPrefWithSubstitutions(
-                  argParser, Arg.TEMPFAC, ArgValuesMap.Position.AFTER, av,
-                  subVals, null, null, null);
+                  argParser, Arg.TEMPFAC, ArgValuesMap.Position.AFTER,
+                  structureAv, subVals, null, null, null);
           boolean notempfac = avm.getFromSubValArgOrPref(Arg.NOTEMPFAC,
                   subVals, null, "ADD_TEMPFACT_ANN", false, true);
           TFType tft = notempfac ? null : TFType.DEFAULT;
@@ -636,8 +634,8 @@ public class Commands
           }
 
           String sViewerName = avm.getFromSubValArgOrPref(
-                  Arg.STRUCTUREVIEWER, ArgValuesMap.Position.AFTER, av,
-                  subVals, null, null, "jmol");
+                  Arg.STRUCTUREVIEWER, ArgValuesMap.Position.AFTER,
+                  structureAv, subVals, null, null, "jmol");
           ViewerType viewerType = ViewerType.getFromString(sViewerName);
 
           // TODO use ssFromStructure
@@ -698,7 +696,8 @@ public class Commands
           if (avm.containsArg(Arg.STRUCTUREIMAGE))
           {
             for (ArgValue structureImageArgValue : avm
-                    .getArgValueList(Arg.STRUCTUREIMAGE))
+                    .getArgValueListFromSubValOrArg(structureAv,
+                            Arg.STRUCTUREIMAGE, subVals))
             {
               String structureImageFilename = argParser.makeSubstitutions(
                       structureImageArgValue.getValue(), id, true);
index d043d16..cdb29bf 100644 (file)
@@ -25,9 +25,11 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import jalview.bin.argparser.Arg.Opt;
@@ -181,7 +183,8 @@ public enum Arg
                   + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
           Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.MULTIVALUE,
           Opt.ALLOWMULTIID, Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
-  STRUCTUREIMAGE(Type.IMAGE,
+  STRUCTUREIMAGE(new Type[]
+  { Type.IMAGE, Type.STRUCTUREIMAGE },
           "Export an image of a 3D structure opened in JMOL", Opt.STRING,
           Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID,
           Opt.PRIMARY),
@@ -213,26 +216,6 @@ public enum Arg
   BGCOLOUR(Type.IMAGE, "bgcolor", // being a bit soft on the Americans!
           "Applies a background colour to the structure image. Valid values are named colours known to Java or RRGGBB 6 digit hex-string.",
           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.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.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.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.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"
                   + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
@@ -499,9 +482,8 @@ 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 or structure"),
-    // 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"),
+    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
@@ -533,29 +515,53 @@ public enum Arg
 
   private String description;
 
-  private Type type;
+  private Type[] types;
 
   private Arg(Type type, String description, Opt... options)
   {
+    this(new Type[] { type }, description, options);
+  }
+
+  private Arg(Type[] type, String description, Opt... options)
+  {
     this(type, null, description, false, options);
   }
 
   private Arg(Type type, String description, boolean defaultBoolean,
           Opt... options)
   {
+    this(new Type[] { type }, description, defaultBoolean, options);
+  }
+
+  private Arg(Type[] type, String description, boolean defaultBoolean,
+          Opt... options)
+  {
     this(type, null, description, defaultBoolean, options);
   }
 
   private Arg(Type type, String alternativeName, String description,
           Opt... options)
   {
+    this(new Type[] { type }, alternativeName, description, options);
+  }
+
+  private Arg(Type[] type, String alternativeName, String description,
+          Opt... options)
+  {
     this(type, alternativeName, description, false, options);
   }
 
   private Arg(Type type, String alternativeName, String description,
           boolean defaultBoolean, Opt... options)
   {
-    this.type = type;
+    this(new Type[] { type }, alternativeName, description, defaultBoolean,
+            options);
+  }
+
+  private Arg(Type[] type, String alternativeName, String description,
+          boolean defaultBoolean, Opt... options)
+  {
+    this.types = type;
     this.description = description;
     this.defaultBoolValue = defaultBoolean;
     this.setOptions(options);
@@ -596,7 +602,11 @@ public enum Arg
     if (getNames().length > 0)
       sb.append('"');
     sb.append(")\n");
-    sb.append("\nType: " + type.name());
+    for (Type type : getTypes())
+    {
+      String typeName = type.name();
+      sb.append("\nType: " + typeName);
+    }
     sb.append("\nOpt: ");
     // map List<Opt> to List<String> for the String.join
     List<String> optList = Arrays.asList(argOptions).stream()
@@ -661,9 +671,39 @@ public enum Arg
     return defaultBoolValue;
   }
 
-  public Type getType()
+  public Type getFirstType()
+  {
+    return this.getTypes()[0];
+  }
+
+  public Type[] getTypes()
   {
-    return this.type;
+    return this.types;
+  }
+
+  public boolean hasType(Type t)
+  {
+    for (Type type : getTypes())
+    {
+      if (type == t)
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean sharesType(Arg a)
+  {
+    List<Type> aTypes = Arrays.asList(a.getTypes());
+    for (Type type : getTypes())
+    {
+      if (aTypes.contains(type))
+      {
+        return true;
+      }
+    }
+    return false;
   }
 
   protected String getDescription()
@@ -690,9 +730,17 @@ public enum Arg
   public static final void appendUsageGeneral(StringBuilder sb,
           int maxArgLength)
   {
+    Set<Type> firstTypes = new HashSet<>();
+    for (Arg a : EnumSet.allOf(Arg.class))
+    {
+      if (!firstTypes.contains(a.getFirstType()))
+      {
+        firstTypes.add(a.getFirstType());
+      }
+    }
     for (Type t : EnumSet.allOf(Type.class))
     {
-      if (t.description() != null)
+      if (t.description() != null && firstTypes.contains(t))
       {
         StringBuilder argSb = new StringBuilder();
         argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
@@ -727,20 +775,6 @@ public enum Arg
     {
       List<Arg> args = argsSortedForDisplay(types);
 
-      /*
-       * just use a set maxArgLength of DESCRIPTIONINDENT
-       
-      int maxArgLength = 0;
-      for (Arg a : args)
-      {
-        if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
-          continue;
-      
-        String argS = argDisplayString(a);
-        if (argS.length() > maxArgLength)
-          maxArgLength = argS.length();
-      }
-      */
       int maxArgLength = DESCRIPTIONINDENT;
 
       // always show --help
@@ -764,10 +798,10 @@ public enum Arg
           continue;
         }
 
-        if (a.getType() != typeSection)
+        if (a.getFirstType() != typeSection)
         {
-          typeSection = a.getType();
-          String typeDescription = a.getType().description();
+          typeSection = a.getFirstType();
+          String typeDescription = a.getFirstType().description();
           if (typeDescription != null && typeDescription.length() > 0)
           {
             // typeDescription = typeDescription.substring(0,
@@ -958,7 +992,7 @@ public enum Arg
   {
     Opt[] opts = options == null ? new Opt[] {} : options;
     return EnumSet.allOf(Arg.class).stream().filter(a -> {
-      if (a.getType() != type)
+      if (!a.hasType(type))
         return false;
       for (Opt o : opts)
       {
@@ -1013,7 +1047,7 @@ class ArgDisplayComparator implements Comparator<Arg>
     if (b == null)
       return -1;
     // first compare types (in enum order)
-    int i = a.getType().compareTo(b.getType());
+    int i = a.getFirstType().compareTo(b.getFirstType());
     if (i != 0)
       return i;
     // do Opt.LAST next (oddly). Reversed args important!
index 155f69e..fe73053 100644 (file)
@@ -661,7 +661,7 @@ 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
+        if (allStructures && (a.hasType(Type.STRUCTURE)
         // || a.getType() == Type.STRUCTUREIMAGE)
         ) && !a.hasOption(Opt.PRIMARY))
         {
index fc1e090..5d53641 100644 (file)
@@ -101,6 +101,96 @@ public class ArgValuesMap
     return avs == null ? new ArrayList<>() : avs.getArgValueList();
   }
 
+  public List<ArgValue> getArgValueListFromSubValOrArg(ArgValue av, Arg a,
+          SubVals sv)
+  {
+    return getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes(
+            null, a, Position.AFTER, av, sv, null, null, null, true);
+  }
+
+  public List<ArgValue> getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes(
+          ArgParser ap, Arg a, ArgValuesMap.Position pos, ArgValue av,
+          SubVals sv, String key, String pref, String def,
+          boolean withinTypes)
+  {
+    if (key == null)
+      key = a.getName();
+    List<ArgValue> avList = new ArrayList<>();
+    if (sv != null && sv.has(key) && sv.get(key) != null)
+    {
+      String value = ap == null ? sv.get(key)
+              : sv.getWithSubstitutions(ap, getLinkedId(), key);
+      // protected ArgValue(Arg a, SubVals sv, Type type, String content, int
+      // argIndex)
+
+      avList.add(new ArgValue(a, null, null, value, av.getArgIndex()));
+    }
+    else if (containsArg(a))
+    {
+      if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null)
+        avList.add(getArgValue(a));
+      else if (pos == ArgValuesMap.Position.BEFORE
+              && getClosestPreviousArgValueOfArg(av, a) != null)
+      {
+        for (ArgValue tmpAv : getArgValues(a).getArgValueList())
+        {
+          if (tmpAv.getArgIndex() >= av.getArgIndex())
+          {
+            continue;
+          }
+          avList.add(tmpAv);
+        }
+      }
+      else if (pos == ArgValuesMap.Position.AFTER
+              && getClosestNextArgValueOfArg(av, a, withinTypes) != null)
+      {
+        for (ArgValue tmpAv : getArgValues(a).getArgValueList())
+        {
+          if (tmpAv.getArgIndex() <= av.getArgIndex())
+          {
+            continue;
+          }
+          avList.add(tmpAv);
+        }
+      }
+    }
+
+    // check if withinType the avs don't belong to the next primary arg
+    // of this type. Checking for *any* shared type.
+    if (withinTypes && !avList.isEmpty())
+    {
+      int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE;
+      // run through every Arg used in this ArgValuesMap
+      for (Arg tmpA : this.getArgKeys())
+      {
+        // only interested in Opt.PRIMARY args of the same type
+        if (tmpA.sharesType(a) && tmpA.hasOption(Opt.PRIMARY))
+        {
+          for (ArgValue tmpAv : getArgValueList(tmpA))
+          {
+            int tmpArgIndex = tmpAv.getArgIndex();
+            if (tmpArgIndex > av.getArgIndex()
+                    && tmpArgIndex < nextPrimaryArgOfSameTypeIndex)
+            {
+              nextPrimaryArgOfSameTypeIndex = tmpArgIndex;
+            }
+          }
+        }
+      }
+      List<ArgValue> tmpList = List.copyOf(avList);
+      for (ArgValue tmpAv : tmpList)
+      {
+        if (nextPrimaryArgOfSameTypeIndex < tmpAv.getArgIndex())
+        {
+          // looks like this tmpAv actually belongs to a different primary Arg
+          avList.remove(tmpAv);
+        }
+      }
+    }
+
+    return avList;
+  }
+
   public ArgValue getArgValue(Arg a)
   {
     List<ArgValue> vals = getArgValueList(a);
@@ -193,7 +283,7 @@ public class ArgValuesMap
   }
 
   public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a,
-          boolean withinType)
+          boolean withinTypes)
   {
     // 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
@@ -215,14 +305,14 @@ public class ArgValuesMap
     }
 
     // check if withinType this closestAv doesn't belong to the next primary arg
-    // of this type
-    if (withinType && closestAv != null)
+    // of this type. Checking for *any* shared type.
+    if (withinTypes && 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))
+        if (tmpA.sharesType(a) && tmpA.hasOption(Opt.PRIMARY))
         {
           for (ArgValue tmpAv : getArgValueList(tmpA))
           {
@@ -237,7 +327,7 @@ public class ArgValuesMap
       }
       if (nextPrimaryArgOfSameTypeIndex < closestAv.getArgIndex())
       {
-        // looks licke closestAv actually belongs to a different primary Arg
+        // looks like closestAv actually belongs to a different primary Arg
         return null;
       }
     }
@@ -370,33 +460,32 @@ public class ArgValuesMap
   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);
+    return getFromSubValArgOrPref(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 String getFromSubValArgOrPref(Arg a, ArgValuesMap.Position pos,
-          ArgValue av, SubVals sv, String key, String pref, String def)
+  public String getFromSubValArgOrPref(Arg a, 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)
+          Position pos, ArgValue av, SubVals sv, String key, String pref,
+          String def)
   {
-    return getFromSubValArgOrPrefWithSubstitutionsWithinType(ap, a, pos, av,
-            sv, key, pref, def, true);
+    return getFromSubValArgOrPrefWithSubstitutionsWithinTypes(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)
+  public String getFromSubValArgOrPrefWithSubstitutionsWithinTypes(
+          ArgParser ap, Arg a, Position pos, ArgValue av, SubVals sv,
+          String key, String pref, String def, boolean withinTypes)
   {
     if (key == null)
       key = a.getName();
@@ -414,14 +503,14 @@ public class ArgValuesMap
               && 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();
+              && getClosestNextArgValueOfArg(av, a, withinTypes) != null)
+        value = getClosestNextArgValueOfArg(av, a, withinTypes).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
+              && arg.hasType(Type.STRUCTURE) && !a.hasOption(Opt.PRIMARY)
+              && (a.getFirstType() == Type.STRUCTURE
               // || a.getType() == Type.STRUCTUREIMAGE))
               ))
       {
index 80e08c5..51f8147 100644 (file)
@@ -160,10 +160,12 @@ public class BootstrapArgs
               argsOptions.add(opt);
             }
           }
-          Type t = a.getType();
-          if (!argsTypes.contains(t))
+          for (Type t : a.getTypes())
           {
-            argsTypes.add(t);
+            if (!argsTypes.contains(t))
+            {
+              argsTypes.add(t);
+            }
           }
         }