JAL-629 Fix --tempfac. Hide non-working --notempfac. Add --scale, --width, --height...
[jalview.git] / src / jalview / bin / argparser / ArgParser.java
index 415e065..223cc53 100644 (file)
@@ -98,8 +98,9 @@ public class ArgParser
   private static final String ARGFILEDIRNAME = "{argfiledirname}";
 
   // flag to say whether {n} subtitutions in output filenames should be made.
-  // Turn on and off with --subs and --nosubs
-  private boolean substitutions = false;
+  // Turn on and off with --substitutions and --nosubstitutions
+  // Start with it on
+  private boolean substitutions = true;
 
   // flag to say whether the default linkedId is the current default linked id
   // or ALL linkedIds
@@ -117,6 +118,8 @@ public class ArgParser
 
   private int argIndex = 0;
 
+  private BootstrapArgs bootstrapArgs = null;
+
   static
   {
     argMap = new HashMap<>();
@@ -146,10 +149,11 @@ public class ArgParser
 
   public ArgParser(String[] args)
   {
-    this(args, false);
+    this(args, false, null);
   }
 
-  public ArgParser(String[] args, boolean initsubstitutions)
+  public ArgParser(String[] args, boolean initsubstitutions,
+          BootstrapArgs bsa)
   {
     // Make a mutable new ArrayList so that shell globbing parser works.
     // (When shell file globbing is used, there are a sequence of non-Arg
@@ -157,16 +161,17 @@ public class ArgParser
     // consumed by the --append/--argfile/etc Arg which is most easily done by
     // removing these filenames from the list one at a time. This can't be done
     // with an ArrayList made with only Arrays.asList(String[] args). )
-    this(new ArrayList<>(Arrays.asList(args)), initsubstitutions);
+    this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
+            bsa);
   }
 
   public ArgParser(List<String> args, boolean initsubstitutions)
   {
-    this(args, initsubstitutions, false);
+    this(args, initsubstitutions, false, null);
   }
 
   public ArgParser(List<String> args, boolean initsubstitutions,
-          boolean allowPrivate)
+          boolean allowPrivate, BootstrapArgs bsa)
   {
     // do nothing if there are no "--" args and (some "-" args || >0 arg is
     // "open")
@@ -190,6 +195,10 @@ public class ArgParser
       parse(new ArrayList<String>(), false, false);
       return;
     }
+    if (bsa != null)
+      this.bootstrapArgs = bsa;
+    else
+      this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
     parse(args, initsubstitutions, allowPrivate);
   }
 
@@ -261,8 +270,10 @@ public class ArgParser
         {
           // arg not found
           Console.error("Argument '" + arg + "' not recognised.  Exiting.");
-          Jalview.exit("Unrecognised command line argument '" + arg + "'",
-                  13);
+          Jalview.exit("Invalid argument used." + System.lineSeparator()
+                  + "Use" + System.lineSeparator() + "jalview "
+                  + Arg.HELP.argString() + System.lineSeparator()
+                  + "for a usage statement.", 13);
           continue;
         }
         if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
@@ -346,11 +357,7 @@ public class ArgParser
 
         // make NOACTION adjustments
         // default and auto counter increments
-        if (a == Arg.NEWFRAME)
-        {
-          defaultLinkedIdCounter++;
-        }
-        else if (a == Arg.NPP)
+        if (a == Arg.NPP)
         {
           linkedIdAutoCounter++;
         }
@@ -366,11 +373,18 @@ public class ArgParser
         {
           argFile = null;
         }
-        else if (a == Arg.ALLFRAMES)
+        else if (a == Arg.ALL)
         {
           allLinkedIds = !negated;
         }
 
+        // this is probably only Arg.NEW and Arg.OPEN
+        if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
+        {
+          // use the next default prefixed OPENLINKEDID
+          defaultLinkedId(true);
+        }
+
         String autoCounterString = null;
         boolean usingAutoCounterLinkedId = false;
         String defaultLinkedId = defaultLinkedId(false);
@@ -379,11 +393,6 @@ public class ArgParser
         {
           if (linkedId == null)
           {
-            if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
-            {
-              // use the next default prefixed OPENLINKEDID
-              defaultLinkedId = defaultLinkedId(true);
-            }
             if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
             {
               linkedId = MATCHALLLINKEDIDS;
@@ -419,14 +428,11 @@ public class ArgParser
           }
         }
 
-        // do not continue for NOACTION args
+        // do not continue in this block for NOACTION args
         if (a.hasOption(Opt.NOACTION))
           continue;
 
-        if (!linkedArgs.containsKey(linkedId))
-          linkedArgs.put(linkedId, new ArgValuesMap());
-
-        ArgValuesMap avm = linkedArgs.get(linkedId);
+        ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
 
         // not dealing with both NODUPLICATEVALUES and GLOB
         if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
@@ -536,8 +542,7 @@ public class ArgParser
                 .toString();
       }
     }
-    if (!linkedArgs.containsKey(defaultLinkedId))
-      linkedArgs.put(defaultLinkedId, new ArgValuesMap());
+    getOrCreateLinkedArgValuesMap(defaultLinkedId);
     return defaultLinkedId;
   }
 
@@ -621,9 +626,24 @@ public class ArgParser
     return vals;
   }
 
+  public BootstrapArgs getBootstrapArgs()
+  {
+    return bootstrapArgs;
+  }
+
   public boolean isSet(Arg a)
   {
-    return a.hasOption(Opt.LINKED) ? isSet("", a) : isSet(null, a);
+    return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
+  }
+
+  public boolean isSetAtAll(Arg a)
+  {
+    for (String linkedId : linkedOrder)
+    {
+      if (isSet(linkedId, a))
+        return true;
+    }
+    return false;
   }
 
   public boolean isSet(String linkedId, Arg a)
@@ -632,7 +652,7 @@ public class ArgParser
     return avm == null ? false : avm.containsArg(a);
   }
 
-  public boolean getBool(Arg a)
+  public boolean getBoolean(Arg a)
   {
     if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
     {
@@ -699,7 +719,7 @@ public class ArgParser
   }
 
   public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
-          boolean initsubstitutions)
+          boolean initsubstitutions, BootstrapArgs bsa)
   {
     List<File> argFiles = new ArrayList<>();
 
@@ -709,11 +729,11 @@ public class ArgParser
       argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
     }
 
-    return parseArgFileList(argFiles, initsubstitutions);
+    return parseArgFileList(argFiles, initsubstitutions, bsa);
   }
 
   public static ArgParser parseArgFileList(List<File> argFiles,
-          boolean initsubstitutions)
+          boolean initsubstitutions, BootstrapArgs bsa)
   {
     List<String> argsList = new ArrayList<>();
     for (File argFile : argFiles)
@@ -741,7 +761,7 @@ public class ArgParser
     }
     // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
     // --unsetargfile
-    return new ArgParser(argsList, initsubstitutions, true);
+    return new ArgParser(argsList, initsubstitutions, true, bsa);
   }
 
   protected static List<String> readArgFile(File argFile)
@@ -773,40 +793,60 @@ public class ArgParser
     FIRST, BEFORE, AFTER
   }
 
-  public static String getValueFromSubValOrArg(ArgValuesMap avm, Arg a,
-          SubVals sv)
+  // 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, a, sv, null, null, null);
+    return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
   }
 
-  public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
-          SubVals sv, String key, String pref, String def)
+  // get from following Arg of type a or subval key or preference pref or
+  // default def
+  public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
+          Arg a, SubVals sv, String key, String pref, String def)
   {
-    return getFromSubValArgOrPref(avm, a, Position.FIRST, null, sv, key,
-            pref, 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)
-      return sv.get(key);
-    if (avm != null && avm.containsArg(a))
     {
-      String val = 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)
-        return avm.getValue(a);
+        value = avm.getValue(a);
       else if (pos == Position.BEFORE
               && avm.getClosestPreviousArgValueOfArg(av, a) != null)
-        return avm.getClosestPreviousArgValueOfArg(av, a).getValue();
+        value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
       else if (pos == Position.AFTER
               && avm.getClosestNextArgValueOfArg(av, a) != null)
-        return avm.getClosestNextArgValueOfArg(av, a).getValue();
+        value = avm.getClosestNextArgValueOfArg(av, a).getValue();
     }
-    return pref != null ? Cache.getDefault(pref, def) : def;
+    else
+    {
+      value = pref != null ? Cache.getDefault(pref, def) : def;
+    }
+    return value;
   }
 
   public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
@@ -818,12 +858,59 @@ public class ArgParser
   public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
           SubVals sv, String key, String pref, boolean def)
   {
+    if ((key == null && a == null) || (sv == null && a == null))
+      return false;
+
+    boolean usingArgKey = false;
     if (key == null)
+    {
       key = a.getName();
-    if (sv != null && sv.has(key) && sv.get(key) != null)
-      return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
+      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
     return pref != null ? Cache.getDefault(pref, def) : def;
   }
 
@@ -968,4 +1055,14 @@ public class ArgParser
     }
   }
 
+  private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
+  {
+    if (linkedArgs.containsKey(linkedId)
+            && linkedArgs.get(linkedId) != null)
+      return linkedArgs.get(linkedId);
+
+    linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
+    return linkedArgs.get(linkedId);
+  }
+
 }
\ No newline at end of file