2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.bin.argparser;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.EnumSet;
31 import java.util.Enumeration;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Locale;
37 import jalview.bin.Cache;
38 import jalview.bin.Console;
39 import jalview.bin.Jalview;
40 import jalview.bin.argparser.Arg.Opt;
41 import jalview.util.FileUtils;
42 import jalview.util.HttpUtils;
44 public class ArgParser
46 protected static final String DOUBLEDASH = "--";
48 protected static final char EQUALS = '=';
50 protected static final String NEGATESTRING = "no";
52 // the default linked id prefix used for no id (not even square braces)
53 protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
55 // the linkedId string used to match all linkedIds seen so far
56 protected static final String MATCHALLLINKEDIDS = "*";
58 // the counter added to the default linked id prefix
59 private int defaultLinkedIdCounter = 0;
61 // the substitution string used to use the defaultLinkedIdCounter
62 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
64 // the counter added to the default linked id prefix. NOW using
65 // linkedIdAutoCounter
66 // private int openLinkedIdCounter = 0;
68 // the linked id prefix used for --open files. NOW the same as DEFAULT
69 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
71 // the counter used for {n} substitutions
72 private int linkedIdAutoCounter = 0;
74 // the linked id substitution string used to increment the idCounter (and use
75 // the incremented value)
76 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
78 // the linked id substitution string used to use the idCounter
79 private static final String LINKEDIDAUTOCOUNTER = "{n}";
81 // the linked id substitution string used to use the base filename of --append
83 private static final String LINKEDIDBASENAME = "{basename}";
85 // the linked id substitution string used to use the dir path of --append
87 private static final String LINKEDIDDIRNAME = "{dirname}";
89 // the current argfile
90 private String argFile = null;
92 // the linked id substitution string used to use the dir path of the latest
94 private static final String ARGFILEBASENAME = "{argfilebasename}";
96 // the linked id substitution string used to use the dir path of the latest
98 private static final String ARGFILEDIRNAME = "{argfiledirname}";
100 // an output file wildcard to signify --output=*.ext is really --all --output
102 private static final String OUTPUTWILDCARD = "*.";
104 // flag to say whether {n} subtitutions in output filenames should be made.
105 // Turn on and off with --substitutions and --nosubstitutions
107 private boolean substitutions = true;
109 // flag to say whether the default linkedId is the current default linked id
111 private boolean allLinkedIds = false;
113 protected static final Map<String, Arg> argMap;
115 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
117 protected List<String> linkedOrder = new ArrayList<>();
119 protected List<Arg> argList = new ArrayList<>();
121 private static final char ARGFILECOMMENT = '#';
123 private int argIndex = 0;
125 private BootstrapArgs bootstrapArgs = null;
129 argMap = new HashMap<>();
130 for (Arg a : EnumSet.allOf(Arg.class))
132 for (String argName : a.getNames())
134 if (argMap.containsKey(argName))
136 Console.warn("Trying to add argument name multiple times: '"
137 + argName + "'"); // RESTORE THIS WHEN
139 if (argMap.get(argName) != a)
142 "Trying to add argument name multiple times for different Args: '"
143 + argMap.get(argName).getName() + ":" + argName
144 + "' and '" + a.getName() + ":" + argName
149 argMap.put(argName, a);
154 public ArgParser(String[] args)
156 this(args, false, null);
159 public ArgParser(String[] args, boolean initsubstitutions,
162 // Make a mutable new ArrayList so that shell globbing parser works.
163 // (When shell file globbing is used, there are a sequence of non-Arg
164 // arguments (which are the expanded globbed filenames) that need to be
165 // consumed by the --append/--argfile/etc Arg which is most easily done by
166 // removing these filenames from the list one at a time. This can't be done
167 // with an ArrayList made with only Arrays.asList(String[] args). )
168 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
172 public ArgParser(List<String> args, boolean initsubstitutions)
174 this(args, initsubstitutions, false, null);
177 public ArgParser(List<String> args, boolean initsubstitutions,
178 boolean allowPrivate, BootstrapArgs bsa)
180 // do nothing if there are no "--" args and (some "-" args || >0 arg is
184 for (String arg : args)
186 if (arg.startsWith(DOUBLEDASH))
191 else if (arg.startsWith("-") || arg.equals("open"))
198 // leave it to the old style -- parse an empty list
199 parse(new ArrayList<String>(), false, false);
203 this.bootstrapArgs = bsa;
205 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
206 parse(args, initsubstitutions, allowPrivate);
209 private void parse(List<String> args, boolean initsubstitutions,
210 boolean allowPrivate)
212 this.substitutions = initsubstitutions;
213 boolean openEachInitialFilenames = true;
214 for (int i = 0; i < args.size(); i++)
216 String arg = args.get(i);
218 // If the first arguments do not start with "--" or "-" or is not "open"
219 // and` is a filename that exists it is probably a file/list of files to
220 // open so we fake an Arg.OPEN argument and when adding files only add the
221 // single arg[i] and increment the defaultLinkedIdCounter so that each of
222 // these files is opened separately.
223 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
224 && !arg.startsWith("-") && !arg.equals("open")
225 && (new File(arg).exists()
226 || HttpUtils.startsWithHttpOrHttps(arg)))
228 arg = Arg.OPEN.argString();
232 openEachInitialFilenames = false;
235 String argName = null;
237 List<String> globVals = null; // for Opt.GLOB only
238 SubVals globSubVals = null; // also for use by Opt.GLOB only
239 String linkedId = null;
240 if (arg.startsWith(DOUBLEDASH))
242 int equalPos = arg.indexOf(EQUALS);
245 argName = arg.substring(DOUBLEDASH.length(), equalPos);
246 val = arg.substring(equalPos + 1);
250 argName = arg.substring(DOUBLEDASH.length());
252 int idOpen = argName.indexOf('[');
253 int idClose = argName.indexOf(']');
255 if (idOpen > -1 && idClose == argName.length() - 1)
257 linkedId = argName.substring(idOpen + 1, idClose);
258 argName = argName.substring(0, idOpen);
261 Arg a = argMap.get(argName);
262 // check for boolean prepended by "no"
263 boolean negated = false;
264 if (a == null && argName.startsWith(NEGATESTRING) && argMap
265 .containsKey(argName.substring(NEGATESTRING.length())))
267 argName = argName.substring(NEGATESTRING.length());
268 a = argMap.get(argName);
272 // check for config errors
276 Console.error("Argument '" + arg + "' not recognised. Exiting.");
277 Jalview.exit("Invalid argument used." + System.lineSeparator()
278 + "Use" + System.lineSeparator() + "jalview "
279 + Arg.HELP.argString() + System.lineSeparator()
280 + "for a usage statement.", 13);
283 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
286 "Argument '" + a.argString() + "' is private. Ignoring.");
289 if (!a.hasOption(Opt.BOOLEAN) && negated)
291 // used "no" with a non-boolean option
292 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
293 + "' not a boolean option. Ignoring.");
296 if (!a.hasOption(Opt.STRING) && equalPos > -1)
298 // set --argname=value when arg does not accept values
299 Console.error("Argument '" + a.argString()
300 + "' does not expect a value (given as '" + arg
304 if (!a.hasOption(Opt.LINKED) && linkedId != null)
306 // set --argname[linkedId] when arg does not use linkedIds
307 Console.error("Argument '" + a.argString()
308 + "' does not expect a linked id (given as '" + arg
314 if (a.hasOption(Opt.STRING))
318 if (a.hasOption(Opt.GLOB))
320 // strip off and save the SubVals to be added individually later
321 globSubVals = new SubVals(val);
322 // make substitutions before looking for files
323 String fileGlob = makeSubstitutions(globSubVals.getContent(),
325 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
329 // val is already set -- will be saved in the ArgValue later in
335 // There is no "=" so value is next arg or args (possibly shell
337 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
339 // no value to take for arg, which wants a value
340 Console.error("Argument '" + a.getName()
341 + "' requires a value, none given. Ignoring.");
344 // deal with bash globs here (--arg val* is expanded before reaching
345 // the JVM). Note that SubVals cannot be used in this case.
346 // If using the --arg=val then the glob is preserved and Java globs
347 // will be used later. SubVals can be used.
348 if (a.hasOption(Opt.GLOB))
350 // if this is the first argument with a file list at the start of
351 // the args we add filenames from index i instead of i+1
352 globVals = getShellGlobbedFilenameValues(a, args,
353 openEachInitialFilenames ? i : i + 1);
357 val = args.get(i + 1);
362 // make NOACTION adjustments
363 // default and auto counter increments
366 linkedIdAutoCounter++;
368 else if (a == Arg.SUBSTITUTIONS)
370 substitutions = !negated;
372 else if (a == Arg.SETARGFILE)
376 else if (a == Arg.UNSETARGFILE)
380 else if (a == Arg.ALL)
382 allLinkedIds = !negated;
385 // this is probably only Arg.NEW and Arg.OPEN
386 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
388 // use the next default prefixed OPENLINKEDID
389 defaultLinkedId(true);
392 String autoCounterString = null;
393 boolean usingAutoCounterLinkedId = false;
394 String defaultLinkedId = defaultLinkedId(false);
395 boolean usingDefaultLinkedId = false;
396 if (a.hasOption(Opt.LINKED))
398 if (linkedId == null)
400 if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
401 && val.startsWith(OUTPUTWILDCARD))
403 // --output=*.ext is shorthand for --all --output {basename}.ext
404 // (or --image=*.ext)
406 linkedId = MATCHALLLINKEDIDS;
408 val = LINKEDIDBASENAME
409 + val.substring(OUTPUTWILDCARD.length() - 1);
411 else if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
413 linkedId = MATCHALLLINKEDIDS;
417 // use default linkedId for linked arguments
418 linkedId = defaultLinkedId;
419 usingDefaultLinkedId = true;
420 Console.debug("Changing linkedId to '" + linkedId + "' from "
424 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
426 // turn {n} to the autoCounter
427 autoCounterString = Integer.toString(linkedIdAutoCounter);
428 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
430 usingAutoCounterLinkedId = true;
432 "Changing linkedId to '" + linkedId + "' from " + arg);
434 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
436 // turn {++n} to the incremented autoCounter
437 autoCounterString = Integer.toString(++linkedIdAutoCounter);
438 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
440 usingAutoCounterLinkedId = true;
442 "Changing linkedId to '" + linkedId + "' from " + arg);
446 // do not continue in this block for NOACTION args
447 if (a.hasOption(Opt.NOACTION))
450 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
452 // not dealing with both NODUPLICATEVALUES and GLOB
453 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
455 Console.error("Argument '" + a.argString()
456 + "' cannot contain a duplicate value ('" + val
457 + "'). Ignoring this and subsequent occurrences.");
461 // check for unique id
462 SubVals idsv = new SubVals(val);
463 String id = idsv.get(ArgValues.ID);
464 if (id != null && avm.hasId(a, id))
466 Console.error("Argument '" + a.argString()
467 + "' has a duplicate id ('" + id + "'). Ignoring.");
472 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
473 * DONE, need to check
475 ArgValues avs = avm.getOrCreateArgValues(a);
477 // store appropriate String value(s)
478 if (a.hasOption(Opt.STRING))
480 if (a.hasOption(Opt.GLOB) && globVals != null
481 && globVals.size() > 0)
483 Enumeration<String> gve = Collections.enumeration(globVals);
484 while (gve.hasMoreElements())
486 String v = gve.nextElement();
487 SubVals vsv = new SubVals(globSubVals, v);
488 addValue(linkedId, avs, vsv, v, argIndex++, true);
489 // if we're using defaultLinkedId and the arg increments the
491 if (gve.hasMoreElements() && usingDefaultLinkedId
492 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
494 // increment the default linkedId
495 linkedId = defaultLinkedId(true);
496 // get new avm and avs
497 avm = linkedArgs.get(linkedId);
498 avs = avm.getOrCreateArgValues(a);
504 addValue(linkedId, avs, val, argIndex, true);
507 else if (a.hasOption(Opt.BOOLEAN))
509 setBoolean(linkedId, avs, !negated, argIndex);
510 setNegated(linkedId, avs, negated);
512 else if (a.hasOption(Opt.UNARY))
514 setBoolean(linkedId, avs, true, argIndex);
517 // remove the '*' linkedId that should be empty if it was created
518 if (MATCHALLLINKEDIDS.equals(linkedId)
519 && linkedArgs.containsKey(linkedId))
521 linkedArgs.remove(linkedId);
527 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
530 incrementCount(linkedId, avs);
533 // store in appropriate place
534 if (a.hasOption(Opt.LINKED))
536 // store the order of linkedIds
537 if (!linkedOrder.contains(linkedId))
538 linkedOrder.add(linkedId);
541 // store arg in the list of args used
542 if (!argList.contains(a))
546 private String defaultLinkedId(boolean increment)
548 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
549 .append(Integer.toString(defaultLinkedIdCounter)).toString();
552 while (linkedArgs.containsKey(defaultLinkedId))
554 defaultLinkedIdCounter++;
555 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
556 .append(Integer.toString(defaultLinkedIdCounter))
560 getOrCreateLinkedArgValuesMap(defaultLinkedId);
561 return defaultLinkedId;
564 public String makeSubstitutions(String val, String linkedId)
566 if (!this.substitutions || val == null)
571 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
573 int closeBracket = val.indexOf(']');
574 if (val.length() == closeBracket)
576 subvals = val.substring(0, closeBracket + 1);
577 rest = val.substring(closeBracket + 1);
584 if (rest.contains(LINKEDIDAUTOCOUNTER))
585 rest = rest.replace(LINKEDIDAUTOCOUNTER,
586 String.valueOf(linkedIdAutoCounter));
587 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
588 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
589 String.valueOf(++linkedIdAutoCounter));
590 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
591 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
592 String.valueOf(defaultLinkedIdCounter));
593 ArgValuesMap avm = linkedArgs.get(linkedId);
596 if (rest.contains(LINKEDIDBASENAME))
598 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
600 if (rest.contains(LINKEDIDDIRNAME))
602 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
607 if (rest.contains(ARGFILEBASENAME))
609 rest = rest.replace(ARGFILEBASENAME,
610 FileUtils.getBasename(new File(argFile)));
612 if (rest.contains(ARGFILEDIRNAME))
614 rest = rest.replace(ARGFILEDIRNAME,
615 FileUtils.getDirname(new File(argFile)));
619 return new StringBuilder(subvals).append(rest).toString();
623 * A helper method to take a list of String args where we're expecting
624 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
625 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
626 * "file2", "file3"} *and remove these from the original list object* so that
627 * processing can continue from where it has left off, e.g. args has become
628 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
629 * carries on from the next --arg if available.
631 protected static List<String> getShellGlobbedFilenameValues(Arg a,
632 List<String> args, int i)
634 List<String> vals = new ArrayList<>();
635 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
637 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
638 if (!a.hasOption(Opt.GLOB))
644 public BootstrapArgs getBootstrapArgs()
646 return bootstrapArgs;
649 public boolean isSet(Arg a)
651 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
654 public boolean isSetAtAll(Arg a)
656 for (String linkedId : linkedOrder)
658 if (isSet(linkedId, a))
664 public boolean isSet(String linkedId, Arg a)
666 ArgValuesMap avm = linkedArgs.get(linkedId);
667 return avm == null ? false : avm.containsArg(a);
670 public boolean getBoolean(Arg a)
672 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
674 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
677 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
680 public boolean getBool(String linkedId, Arg a)
682 ArgValuesMap avm = linkedArgs.get(linkedId);
684 return a.getDefaultBoolValue();
685 ArgValues avs = avm.getArgValues(a);
686 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
689 public List<String> getLinkedIds()
694 public ArgValuesMap getLinkedArgs(String id)
696 return linkedArgs.get(id);
700 public String toString()
702 StringBuilder sb = new StringBuilder();
703 sb.append("UNLINKED\n");
704 sb.append(argValuesMapToString(linkedArgs.get(null)));
705 if (getLinkedIds() != null)
707 sb.append("LINKED\n");
708 for (String id : getLinkedIds())
710 // already listed these as UNLINKED args
714 ArgValuesMap avm = getLinkedArgs(id);
715 sb.append("ID: '").append(id).append("'\n");
716 sb.append(argValuesMapToString(avm));
719 return sb.toString();
722 private static String argValuesMapToString(ArgValuesMap avm)
726 StringBuilder sb = new StringBuilder();
727 for (Arg a : avm.getArgKeys())
729 ArgValues v = avm.getArgValues(a);
730 sb.append(v.toString());
733 return sb.toString();
736 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
737 boolean initsubstitutions, BootstrapArgs bsa)
739 List<File> argFiles = new ArrayList<>();
741 for (String pattern : argFilenameGlobs)
743 // I don't think we want to dedup files, making life easier
744 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
747 return parseArgFileList(argFiles, initsubstitutions, bsa);
750 public static ArgParser parseArgFileList(List<File> argFiles,
751 boolean initsubstitutions, BootstrapArgs bsa)
753 List<String> argsList = new ArrayList<>();
754 for (File argFile : argFiles)
756 if (!argFile.exists())
758 String message = Arg.ARGFILE.argString() + EQUALS + "\""
759 + argFile.getPath() + "\": File does not exist.";
760 Jalview.exit(message, 2);
764 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
765 .append(EQUALS).append(argFile.getCanonicalPath())
767 argsList.add(setargfile);
768 argsList.addAll(readArgFile(argFile));
769 argsList.add(Arg.UNSETARGFILE.argString());
770 } catch (IOException e)
772 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
773 + "\": File could not be read.";
774 Jalview.exit(message, 3);
777 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
779 return new ArgParser(argsList, initsubstitutions, true, bsa);
782 protected static List<String> readArgFile(File argFile)
784 List<String> args = new ArrayList<>();
785 if (argFile != null && argFile.exists())
789 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
791 if (line != null && line.length() > 0
792 && line.charAt(0) != ARGFILECOMMENT)
795 } catch (IOException e)
797 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
798 + "\": File could not be read.";
799 Console.debug(message, e);
800 Jalview.exit(message, 3);
806 public static enum Position
811 // get from following Arg of type a or subval of same name (lowercase)
812 public static String getValueFromSubValOrArg(ArgValuesMap avm,
813 ArgValue av, Arg a, SubVals sv)
815 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
818 // get from following Arg of type a or subval key or preference pref or
820 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
821 Arg a, SubVals sv, String key, String pref, String def)
823 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
827 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
828 // Arg of type a or subval key or preference pref or default def
829 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
830 Position pos, ArgValue av, SubVals sv, String key, String pref,
833 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
837 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
838 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
839 String key, String pref, String def)
844 if (sv != null && sv.has(key) && sv.get(key) != null)
846 value = ap == null ? sv.get(key)
847 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
849 else if (avm != null && avm.containsArg(a))
851 if (pos == Position.FIRST && avm.getValue(a) != null)
852 value = avm.getValue(a);
853 else if (pos == Position.BEFORE
854 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
855 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
856 else if (pos == Position.AFTER
857 && avm.getClosestNextArgValueOfArg(av, a) != null)
858 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
862 value = pref != null ? Cache.getDefault(pref, def) : def;
867 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
870 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
873 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
874 SubVals sv, String key, String pref, boolean def)
876 if ((key == null && a == null) || (sv == null && a == null))
879 boolean usingArgKey = false;
886 String nokey = ArgParser.NEGATESTRING + key;
888 // look for key or nokey in subvals first (if using Arg check options)
891 // check for true boolean
892 if (sv.has(key) && sv.get(key) != null)
896 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
899 "Looking for boolean in subval from non-boolean/non-unary Arg "
904 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
907 // check for negative boolean (subval "no..." will be "true")
908 if (sv.has(nokey) && sv.get(nokey) != null)
912 if (!(a.hasOption(Opt.BOOLEAN)))
915 "Looking for negative boolean in subval from non-boolean Arg "
920 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
925 if (avm != null && avm.containsArg(a))
926 return avm.getBoolean(a);
928 // return preference or default
929 return pref != null ? Cache.getDefault(pref, def) : def;
932 // the following methods look for the "*" linkedId and add the argvalue to all
933 // linkedId ArgValues if it does
934 private void addValue(String linkedId, ArgValues avs, SubVals sv,
935 String v, int argIndex, boolean doSubs)
938 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
940 for (String id : getLinkedIds())
942 if (id == null || MATCHALLLINKEDIDS.equals(id))
944 ArgValuesMap avm = linkedArgs.get(id);
945 if (a.hasOption(Opt.REQUIREINPUT)
946 && !avm.hasArgWithOption(Opt.INPUT))
948 ArgValues tavs = avm.getOrCreateArgValues(a);
952 val = makeSubstitutions(v, id);
953 sv = new SubVals(sv, val);
955 tavs.addValue(sv, val, argIndex);
956 finaliseStoringArgValue(id, tavs);
964 val = makeSubstitutions(v, linkedId);
965 sv = new SubVals(sv, val);
967 avs.addValue(sv, val, argIndex);
968 finaliseStoringArgValue(linkedId, avs);
972 private void addValue(String linkedId, ArgValues avs, String v,
973 int argIndex, boolean doSubs)
976 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
978 for (String id : getLinkedIds())
980 if (id == null || MATCHALLLINKEDIDS.equals(id))
982 ArgValuesMap avm = linkedArgs.get(id);
983 // don't set an output if there isn't an input
984 if (a.hasOption(Opt.REQUIREINPUT)
985 && !avm.hasArgWithOption(Opt.INPUT))
987 ArgValues tavs = avm.getOrCreateArgValues(a);
988 String val = doSubs ? makeSubstitutions(v, id) : v;
989 tavs.addValue(val, argIndex);
990 finaliseStoringArgValue(id, tavs);
995 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
996 avs.addValue(val, argIndex);
997 finaliseStoringArgValue(linkedId, avs);
1001 private void setBoolean(String linkedId, ArgValues avs, boolean b,
1005 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1007 for (String id : getLinkedIds())
1009 if (id == null || MATCHALLLINKEDIDS.equals(id))
1011 ArgValuesMap avm = linkedArgs.get(id);
1012 if (a.hasOption(Opt.REQUIREINPUT)
1013 && !avm.hasArgWithOption(Opt.INPUT))
1015 ArgValues tavs = avm.getOrCreateArgValues(a);
1016 tavs.setBoolean(b, argIndex);
1017 finaliseStoringArgValue(id, tavs);
1022 avs.setBoolean(b, argIndex);
1023 finaliseStoringArgValue(linkedId, avs);
1027 private void setNegated(String linkedId, ArgValues avs, boolean b)
1030 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1032 for (String id : getLinkedIds())
1034 if (id == null || MATCHALLLINKEDIDS.equals(id))
1036 ArgValuesMap avm = linkedArgs.get(id);
1037 if (a.hasOption(Opt.REQUIREINPUT)
1038 && !avm.hasArgWithOption(Opt.INPUT))
1040 ArgValues tavs = avm.getOrCreateArgValues(a);
1050 private void incrementCount(String linkedId, ArgValues avs)
1053 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1055 for (String id : getLinkedIds())
1057 if (id == null || MATCHALLLINKEDIDS.equals(id))
1059 ArgValuesMap avm = linkedArgs.get(id);
1060 if (a.hasOption(Opt.REQUIREINPUT)
1061 && !avm.hasArgWithOption(Opt.INPUT))
1063 ArgValues tavs = avm.getOrCreateArgValues(a);
1064 tavs.incrementCount();
1069 avs.incrementCount();
1073 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1075 if (linkedArgs.containsKey(linkedId)
1076 && linkedArgs.get(linkedId) != null)
1077 return linkedArgs.get(linkedId);
1079 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1080 return linkedArgs.get(linkedId);