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 // flag to say whether {n} subtitutions in output filenames should be made.
101 // Turn on and off with --substitutions and --nosubstitutions
103 private boolean substitutions = true;
105 // flag to say whether the default linkedId is the current default linked id
107 private boolean allLinkedIds = false;
109 protected static final Map<String, Arg> argMap;
111 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
113 protected List<String> linkedOrder = new ArrayList<>();
115 protected List<Arg> argList = new ArrayList<>();
117 private static final char ARGFILECOMMENT = '#';
119 private int argIndex = 0;
121 private BootstrapArgs bootstrapArgs = null;
125 argMap = new HashMap<>();
126 for (Arg a : EnumSet.allOf(Arg.class))
128 for (String argName : a.getNames())
130 if (argMap.containsKey(argName))
132 Console.warn("Trying to add argument name multiple times: '"
133 + argName + "'"); // RESTORE THIS WHEN
135 if (argMap.get(argName) != a)
138 "Trying to add argument name multiple times for different Args: '"
139 + argMap.get(argName).getName() + ":" + argName
140 + "' and '" + a.getName() + ":" + argName
145 argMap.put(argName, a);
150 public ArgParser(String[] args)
152 this(args, false, null);
155 public ArgParser(String[] args, boolean initsubstitutions,
158 // Make a mutable new ArrayList so that shell globbing parser works.
159 // (When shell file globbing is used, there are a sequence of non-Arg
160 // arguments (which are the expanded globbed filenames) that need to be
161 // consumed by the --append/--argfile/etc Arg which is most easily done by
162 // removing these filenames from the list one at a time. This can't be done
163 // with an ArrayList made with only Arrays.asList(String[] args). )
164 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
168 public ArgParser(List<String> args, boolean initsubstitutions)
170 this(args, initsubstitutions, false, null);
173 public ArgParser(List<String> args, boolean initsubstitutions,
174 boolean allowPrivate, BootstrapArgs bsa)
176 // do nothing if there are no "--" args and (some "-" args || >0 arg is
180 for (String arg : args)
182 if (arg.startsWith(DOUBLEDASH))
187 else if (arg.startsWith("-") || arg.equals("open"))
194 // leave it to the old style -- parse an empty list
195 parse(new ArrayList<String>(), false, false);
199 this.bootstrapArgs = bsa;
201 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
202 parse(args, initsubstitutions, allowPrivate);
205 private void parse(List<String> args, boolean initsubstitutions,
206 boolean allowPrivate)
208 this.substitutions = initsubstitutions;
209 boolean openEachInitialFilenames = true;
210 for (int i = 0; i < args.size(); i++)
212 String arg = args.get(i);
214 // If the first arguments do not start with "--" or "-" or is not "open"
215 // and` is a filename that exists it is probably a file/list of files to
216 // open so we fake an Arg.OPEN argument and when adding files only add the
217 // single arg[i] and increment the defaultLinkedIdCounter so that each of
218 // these files is opened separately.
219 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
220 && !arg.startsWith("-") && !arg.equals("open")
221 && (new File(arg).exists()
222 || HttpUtils.startsWithHttpOrHttps(arg)))
224 arg = Arg.OPEN.argString();
228 openEachInitialFilenames = false;
231 String argName = null;
233 List<String> globVals = null; // for Opt.GLOB only
234 SubVals globSubVals = null; // also for use by Opt.GLOB only
235 String linkedId = null;
236 if (arg.startsWith(DOUBLEDASH))
238 int equalPos = arg.indexOf(EQUALS);
241 argName = arg.substring(DOUBLEDASH.length(), equalPos);
242 val = arg.substring(equalPos + 1);
246 argName = arg.substring(DOUBLEDASH.length());
248 int idOpen = argName.indexOf('[');
249 int idClose = argName.indexOf(']');
251 if (idOpen > -1 && idClose == argName.length() - 1)
253 linkedId = argName.substring(idOpen + 1, idClose);
254 argName = argName.substring(0, idOpen);
257 Arg a = argMap.get(argName);
258 // check for boolean prepended by "no"
259 boolean negated = false;
260 if (a == null && argName.startsWith(NEGATESTRING) && argMap
261 .containsKey(argName.substring(NEGATESTRING.length())))
263 argName = argName.substring(NEGATESTRING.length());
264 a = argMap.get(argName);
268 // check for config errors
272 Console.error("Argument '" + arg + "' not recognised. Exiting.");
273 Jalview.exit("Unrecognised command line argument '" + arg + "'",
277 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
280 "Argument '" + a.argString() + "' is private. Ignoring.");
283 if (!a.hasOption(Opt.BOOLEAN) && negated)
285 // used "no" with a non-boolean option
286 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
287 + "' not a boolean option. Ignoring.");
290 if (!a.hasOption(Opt.STRING) && equalPos > -1)
292 // set --argname=value when arg does not accept values
293 Console.error("Argument '" + a.argString()
294 + "' does not expect a value (given as '" + arg
298 if (!a.hasOption(Opt.LINKED) && linkedId != null)
300 // set --argname[linkedId] when arg does not use linkedIds
301 Console.error("Argument '" + a.argString()
302 + "' does not expect a linked id (given as '" + arg
308 if (a.hasOption(Opt.STRING))
312 if (a.hasOption(Opt.GLOB))
314 // strip off and save the SubVals to be added individually later
315 globSubVals = new SubVals(val);
316 // make substitutions before looking for files
317 String fileGlob = makeSubstitutions(globSubVals.getContent(),
319 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
323 // val is already set -- will be saved in the ArgValue later in
329 // There is no "=" so value is next arg or args (possibly shell
331 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
333 // no value to take for arg, which wants a value
334 Console.error("Argument '" + a.getName()
335 + "' requires a value, none given. Ignoring.");
338 // deal with bash globs here (--arg val* is expanded before reaching
339 // the JVM). Note that SubVals cannot be used in this case.
340 // If using the --arg=val then the glob is preserved and Java globs
341 // will be used later. SubVals can be used.
342 if (a.hasOption(Opt.GLOB))
344 // if this is the first argument with a file list at the start of
345 // the args we add filenames from index i instead of i+1
346 globVals = getShellGlobbedFilenameValues(a, args,
347 openEachInitialFilenames ? i : i + 1);
351 val = args.get(i + 1);
356 // make NOACTION adjustments
357 // default and auto counter increments
360 linkedIdAutoCounter++;
362 else if (a == Arg.SUBSTITUTIONS)
364 substitutions = !negated;
366 else if (a == Arg.SETARGFILE)
370 else if (a == Arg.UNSETARGFILE)
374 else if (a == Arg.ALL)
376 allLinkedIds = !negated;
379 // this is probably only Arg.NEW and Arg.OPEN
380 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
382 // use the next default prefixed OPENLINKEDID
383 defaultLinkedId(true);
386 String autoCounterString = null;
387 boolean usingAutoCounterLinkedId = false;
388 String defaultLinkedId = defaultLinkedId(false);
389 boolean usingDefaultLinkedId = false;
390 if (a.hasOption(Opt.LINKED))
392 if (linkedId == null)
394 if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
396 linkedId = MATCHALLLINKEDIDS;
400 // use default linkedId for linked arguments
401 linkedId = defaultLinkedId;
402 usingDefaultLinkedId = true;
403 Console.debug("Changing linkedId to '" + linkedId + "' from "
407 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
409 // turn {n} to the autoCounter
410 autoCounterString = Integer.toString(linkedIdAutoCounter);
411 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
413 usingAutoCounterLinkedId = true;
415 "Changing linkedId to '" + linkedId + "' from " + arg);
417 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
419 // turn {++n} to the incremented autoCounter
420 autoCounterString = Integer.toString(++linkedIdAutoCounter);
421 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
423 usingAutoCounterLinkedId = true;
425 "Changing linkedId to '" + linkedId + "' from " + arg);
429 // do not continue in this block for NOACTION args
430 if (a.hasOption(Opt.NOACTION))
433 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
435 // not dealing with both NODUPLICATEVALUES and GLOB
436 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
438 Console.error("Argument '" + a.argString()
439 + "' cannot contain a duplicate value ('" + val
440 + "'). Ignoring this and subsequent occurrences.");
444 // check for unique id
445 SubVals idsv = new SubVals(val);
446 String id = idsv.get(ArgValues.ID);
447 if (id != null && avm.hasId(a, id))
449 Console.error("Argument '" + a.argString()
450 + "' has a duplicate id ('" + id + "'). Ignoring.");
455 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
456 * DONE, need to check
458 ArgValues avs = avm.getOrCreateArgValues(a);
460 // store appropriate String value(s)
461 if (a.hasOption(Opt.STRING))
463 if (a.hasOption(Opt.GLOB) && globVals != null
464 && globVals.size() > 0)
466 Enumeration<String> gve = Collections.enumeration(globVals);
467 while (gve.hasMoreElements())
469 String v = gve.nextElement();
470 SubVals vsv = new SubVals(globSubVals, v);
471 addValue(linkedId, avs, vsv, v, argIndex++, true);
472 // if we're using defaultLinkedId and the arg increments the
474 if (gve.hasMoreElements() && usingDefaultLinkedId
475 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
477 // increment the default linkedId
478 linkedId = defaultLinkedId(true);
479 // get new avm and avs
480 avm = linkedArgs.get(linkedId);
481 avs = avm.getOrCreateArgValues(a);
487 addValue(linkedId, avs, val, argIndex, true);
490 else if (a.hasOption(Opt.BOOLEAN))
492 setBoolean(linkedId, avs, !negated, argIndex);
493 setNegated(linkedId, avs, negated);
495 else if (a.hasOption(Opt.UNARY))
497 setBoolean(linkedId, avs, true, argIndex);
500 // remove the '*' linkedId that should be empty if it was created
501 if (MATCHALLLINKEDIDS.equals(linkedId)
502 && linkedArgs.containsKey(linkedId))
504 linkedArgs.remove(linkedId);
510 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
513 incrementCount(linkedId, avs);
516 // store in appropriate place
517 if (a.hasOption(Opt.LINKED))
519 // store the order of linkedIds
520 if (!linkedOrder.contains(linkedId))
521 linkedOrder.add(linkedId);
524 // store arg in the list of args used
525 if (!argList.contains(a))
529 private String defaultLinkedId(boolean increment)
531 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
532 .append(Integer.toString(defaultLinkedIdCounter)).toString();
535 while (linkedArgs.containsKey(defaultLinkedId))
537 defaultLinkedIdCounter++;
538 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
539 .append(Integer.toString(defaultLinkedIdCounter))
543 getOrCreateLinkedArgValuesMap(defaultLinkedId);
544 return defaultLinkedId;
547 public String makeSubstitutions(String val, String linkedId)
549 if (!this.substitutions || val == null)
554 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
556 int closeBracket = val.indexOf(']');
557 if (val.length() == closeBracket)
559 subvals = val.substring(0, closeBracket + 1);
560 rest = val.substring(closeBracket + 1);
567 if (rest.contains(LINKEDIDAUTOCOUNTER))
568 rest = rest.replace(LINKEDIDAUTOCOUNTER,
569 String.valueOf(linkedIdAutoCounter));
570 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
571 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
572 String.valueOf(++linkedIdAutoCounter));
573 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
574 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
575 String.valueOf(defaultLinkedIdCounter));
576 ArgValuesMap avm = linkedArgs.get(linkedId);
579 if (rest.contains(LINKEDIDBASENAME))
581 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
583 if (rest.contains(LINKEDIDDIRNAME))
585 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
590 if (rest.contains(ARGFILEBASENAME))
592 rest = rest.replace(ARGFILEBASENAME,
593 FileUtils.getBasename(new File(argFile)));
595 if (rest.contains(ARGFILEDIRNAME))
597 rest = rest.replace(ARGFILEDIRNAME,
598 FileUtils.getDirname(new File(argFile)));
602 return new StringBuilder(subvals).append(rest).toString();
606 * A helper method to take a list of String args where we're expecting
607 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
608 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
609 * "file2", "file3"} *and remove these from the original list object* so that
610 * processing can continue from where it has left off, e.g. args has become
611 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
612 * carries on from the next --arg if available.
614 protected static List<String> getShellGlobbedFilenameValues(Arg a,
615 List<String> args, int i)
617 List<String> vals = new ArrayList<>();
618 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
620 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
621 if (!a.hasOption(Opt.GLOB))
627 public BootstrapArgs getBootstrapArgs()
629 return bootstrapArgs;
632 public boolean isSet(Arg a)
634 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
637 public boolean isSetAtAll(Arg a)
639 for (String linkedId : linkedOrder)
641 if (isSet(linkedId, a))
647 public boolean isSet(String linkedId, Arg a)
649 ArgValuesMap avm = linkedArgs.get(linkedId);
650 return avm == null ? false : avm.containsArg(a);
653 public boolean getBoolean(Arg a)
655 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
657 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
660 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
663 public boolean getBool(String linkedId, Arg a)
665 ArgValuesMap avm = linkedArgs.get(linkedId);
667 return a.getDefaultBoolValue();
668 ArgValues avs = avm.getArgValues(a);
669 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
672 public List<String> getLinkedIds()
677 public ArgValuesMap getLinkedArgs(String id)
679 return linkedArgs.get(id);
683 public String toString()
685 StringBuilder sb = new StringBuilder();
686 sb.append("UNLINKED\n");
687 sb.append(argValuesMapToString(linkedArgs.get(null)));
688 if (getLinkedIds() != null)
690 sb.append("LINKED\n");
691 for (String id : getLinkedIds())
693 // already listed these as UNLINKED args
697 ArgValuesMap avm = getLinkedArgs(id);
698 sb.append("ID: '").append(id).append("'\n");
699 sb.append(argValuesMapToString(avm));
702 return sb.toString();
705 private static String argValuesMapToString(ArgValuesMap avm)
709 StringBuilder sb = new StringBuilder();
710 for (Arg a : avm.getArgKeys())
712 ArgValues v = avm.getArgValues(a);
713 sb.append(v.toString());
716 return sb.toString();
719 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
720 boolean initsubstitutions, BootstrapArgs bsa)
722 List<File> argFiles = new ArrayList<>();
724 for (String pattern : argFilenameGlobs)
726 // I don't think we want to dedup files, making life easier
727 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
730 return parseArgFileList(argFiles, initsubstitutions, bsa);
733 public static ArgParser parseArgFileList(List<File> argFiles,
734 boolean initsubstitutions, BootstrapArgs bsa)
736 List<String> argsList = new ArrayList<>();
737 for (File argFile : argFiles)
739 if (!argFile.exists())
741 String message = Arg.ARGFILE.argString() + EQUALS + "\""
742 + argFile.getPath() + "\": File does not exist.";
743 Jalview.exit(message, 2);
747 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
748 .append(EQUALS).append(argFile.getCanonicalPath())
750 argsList.add(setargfile);
751 argsList.addAll(readArgFile(argFile));
752 argsList.add(Arg.UNSETARGFILE.argString());
753 } catch (IOException e)
755 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
756 + "\": File could not be read.";
757 Jalview.exit(message, 3);
760 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
762 return new ArgParser(argsList, initsubstitutions, true, bsa);
765 protected static List<String> readArgFile(File argFile)
767 List<String> args = new ArrayList<>();
768 if (argFile != null && argFile.exists())
772 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
774 if (line != null && line.length() > 0
775 && line.charAt(0) != ARGFILECOMMENT)
778 } catch (IOException e)
780 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
781 + "\": File could not be read.";
782 Console.debug(message, e);
783 Jalview.exit(message, 3);
789 public static enum Position
794 // get from following Arg of type a or subval of same name (lowercase)
795 public static String getValueFromSubValOrArg(ArgValuesMap avm,
796 ArgValue av, Arg a, SubVals sv)
798 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
801 // get from following Arg of type a or subval key or preference pref or
803 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
804 Arg a, SubVals sv, String key, String pref, String def)
806 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
810 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
811 // Arg of type a or subval key or preference pref or default def
812 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
813 Position pos, ArgValue av, SubVals sv, String key, String pref,
816 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
820 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
821 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
822 String key, String pref, String def)
827 if (sv != null && sv.has(key) && sv.get(key) != null)
829 value = ap == null ? sv.get(key)
830 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
832 else if (avm != null && avm.containsArg(a))
834 if (pos == Position.FIRST && avm.getValue(a) != null)
835 value = avm.getValue(a);
836 else if (pos == Position.BEFORE
837 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
838 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
839 else if (pos == Position.AFTER
840 && avm.getClosestNextArgValueOfArg(av, a) != null)
841 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
845 value = pref != null ? Cache.getDefault(pref, def) : def;
850 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
853 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
856 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
857 SubVals sv, String key, String pref, boolean def)
859 if ((key == null && a == null) || (sv == null && a == null))
862 boolean usingArgKey = false;
869 String nokey = ArgParser.NEGATESTRING + key;
871 // look for key or nokey in subvals first (if using Arg check options)
874 // check for true boolean
875 if (sv.has(key) && sv.get(key) != null)
879 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
882 "Looking for boolean in subval from non-boolean/non-unary Arg "
887 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
890 // check for negative boolean (subval "no..." will be "true")
891 if (sv.has(nokey) && sv.get(nokey) != null)
895 if (!(a.hasOption(Opt.BOOLEAN)))
898 "Looking for negative boolean in subval from non-boolean Arg "
903 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
908 if (avm != null && avm.containsArg(a))
909 return avm.getBoolean(a);
911 // return preference or default
912 return pref != null ? Cache.getDefault(pref, def) : def;
915 // the following methods look for the "*" linkedId and add the argvalue to all
916 // linkedId ArgValues if it does
917 private void addValue(String linkedId, ArgValues avs, SubVals sv,
918 String v, int argIndex, boolean doSubs)
921 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
923 for (String id : getLinkedIds())
925 if (id == null || MATCHALLLINKEDIDS.equals(id))
927 ArgValuesMap avm = linkedArgs.get(id);
928 if (a.hasOption(Opt.REQUIREINPUT)
929 && !avm.hasArgWithOption(Opt.INPUT))
931 ArgValues tavs = avm.getOrCreateArgValues(a);
935 val = makeSubstitutions(v, id);
936 sv = new SubVals(sv, val);
938 tavs.addValue(sv, val, argIndex);
939 finaliseStoringArgValue(id, tavs);
947 val = makeSubstitutions(v, linkedId);
948 sv = new SubVals(sv, val);
950 avs.addValue(sv, val, argIndex);
951 finaliseStoringArgValue(linkedId, avs);
955 private void addValue(String linkedId, ArgValues avs, String v,
956 int argIndex, boolean doSubs)
959 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
961 for (String id : getLinkedIds())
963 if (id == null || MATCHALLLINKEDIDS.equals(id))
965 ArgValuesMap avm = linkedArgs.get(id);
966 // don't set an output if there isn't an input
967 if (a.hasOption(Opt.REQUIREINPUT)
968 && !avm.hasArgWithOption(Opt.INPUT))
970 ArgValues tavs = avm.getOrCreateArgValues(a);
971 String val = doSubs ? makeSubstitutions(v, id) : v;
972 tavs.addValue(val, argIndex);
973 finaliseStoringArgValue(id, tavs);
978 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
979 avs.addValue(val, argIndex);
980 finaliseStoringArgValue(linkedId, avs);
984 private void setBoolean(String linkedId, ArgValues avs, boolean b,
988 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
990 for (String id : getLinkedIds())
992 if (id == null || MATCHALLLINKEDIDS.equals(id))
994 ArgValuesMap avm = linkedArgs.get(id);
995 if (a.hasOption(Opt.REQUIREINPUT)
996 && !avm.hasArgWithOption(Opt.INPUT))
998 ArgValues tavs = avm.getOrCreateArgValues(a);
999 tavs.setBoolean(b, argIndex);
1000 finaliseStoringArgValue(id, tavs);
1005 avs.setBoolean(b, argIndex);
1006 finaliseStoringArgValue(linkedId, avs);
1010 private void setNegated(String linkedId, ArgValues avs, boolean b)
1013 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1015 for (String id : getLinkedIds())
1017 if (id == null || MATCHALLLINKEDIDS.equals(id))
1019 ArgValuesMap avm = linkedArgs.get(id);
1020 if (a.hasOption(Opt.REQUIREINPUT)
1021 && !avm.hasArgWithOption(Opt.INPUT))
1023 ArgValues tavs = avm.getOrCreateArgValues(a);
1033 private void incrementCount(String linkedId, ArgValues avs)
1036 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1038 for (String id : getLinkedIds())
1040 if (id == null || MATCHALLLINKEDIDS.equals(id))
1042 ArgValuesMap avm = linkedArgs.get(id);
1043 if (a.hasOption(Opt.REQUIREINPUT)
1044 && !avm.hasArgWithOption(Opt.INPUT))
1046 ArgValues tavs = avm.getOrCreateArgValues(a);
1047 tavs.incrementCount();
1052 avs.incrementCount();
1056 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1058 if (linkedArgs.containsKey(linkedId)
1059 && linkedArgs.get(linkedId) != null)
1060 return linkedArgs.get(linkedId);
1062 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1063 return linkedArgs.get(linkedId);