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("Invalid argument used." + System.lineSeparator()
274 + "Use" + System.lineSeparator() + "jalview "
275 + Arg.HELP.argString() + System.lineSeparator()
276 + "for a usage statement.", 13);
279 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
282 "Argument '" + a.argString() + "' is private. Ignoring.");
285 if (!a.hasOption(Opt.BOOLEAN) && negated)
287 // used "no" with a non-boolean option
288 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
289 + "' not a boolean option. Ignoring.");
292 if (!a.hasOption(Opt.STRING) && equalPos > -1)
294 // set --argname=value when arg does not accept values
295 Console.error("Argument '" + a.argString()
296 + "' does not expect a value (given as '" + arg
300 if (!a.hasOption(Opt.LINKED) && linkedId != null)
302 // set --argname[linkedId] when arg does not use linkedIds
303 Console.error("Argument '" + a.argString()
304 + "' does not expect a linked id (given as '" + arg
310 if (a.hasOption(Opt.STRING))
314 if (a.hasOption(Opt.GLOB))
316 // strip off and save the SubVals to be added individually later
317 globSubVals = new SubVals(val);
318 // make substitutions before looking for files
319 String fileGlob = makeSubstitutions(globSubVals.getContent(),
321 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
325 // val is already set -- will be saved in the ArgValue later in
331 // There is no "=" so value is next arg or args (possibly shell
333 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
335 // no value to take for arg, which wants a value
336 Console.error("Argument '" + a.getName()
337 + "' requires a value, none given. Ignoring.");
340 // deal with bash globs here (--arg val* is expanded before reaching
341 // the JVM). Note that SubVals cannot be used in this case.
342 // If using the --arg=val then the glob is preserved and Java globs
343 // will be used later. SubVals can be used.
344 if (a.hasOption(Opt.GLOB))
346 // if this is the first argument with a file list at the start of
347 // the args we add filenames from index i instead of i+1
348 globVals = getShellGlobbedFilenameValues(a, args,
349 openEachInitialFilenames ? i : i + 1);
353 val = args.get(i + 1);
358 // make NOACTION adjustments
359 // default and auto counter increments
362 linkedIdAutoCounter++;
364 else if (a == Arg.SUBSTITUTIONS)
366 substitutions = !negated;
368 else if (a == Arg.SETARGFILE)
372 else if (a == Arg.UNSETARGFILE)
376 else if (a == Arg.ALL)
378 allLinkedIds = !negated;
381 // this is probably only Arg.NEW and Arg.OPEN
382 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
384 // use the next default prefixed OPENLINKEDID
385 defaultLinkedId(true);
388 String autoCounterString = null;
389 boolean usingAutoCounterLinkedId = false;
390 String defaultLinkedId = defaultLinkedId(false);
391 boolean usingDefaultLinkedId = false;
392 if (a.hasOption(Opt.LINKED))
394 if (linkedId == null)
396 if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
398 linkedId = MATCHALLLINKEDIDS;
402 // use default linkedId for linked arguments
403 linkedId = defaultLinkedId;
404 usingDefaultLinkedId = true;
405 Console.debug("Changing linkedId to '" + linkedId + "' from "
409 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
411 // turn {n} to the autoCounter
412 autoCounterString = Integer.toString(linkedIdAutoCounter);
413 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
415 usingAutoCounterLinkedId = true;
417 "Changing linkedId to '" + linkedId + "' from " + arg);
419 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
421 // turn {++n} to the incremented autoCounter
422 autoCounterString = Integer.toString(++linkedIdAutoCounter);
423 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
425 usingAutoCounterLinkedId = true;
427 "Changing linkedId to '" + linkedId + "' from " + arg);
431 // do not continue in this block for NOACTION args
432 if (a.hasOption(Opt.NOACTION))
435 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
437 // not dealing with both NODUPLICATEVALUES and GLOB
438 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
440 Console.error("Argument '" + a.argString()
441 + "' cannot contain a duplicate value ('" + val
442 + "'). Ignoring this and subsequent occurrences.");
446 // check for unique id
447 SubVals idsv = new SubVals(val);
448 String id = idsv.get(ArgValues.ID);
449 if (id != null && avm.hasId(a, id))
451 Console.error("Argument '" + a.argString()
452 + "' has a duplicate id ('" + id + "'). Ignoring.");
457 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
458 * DONE, need to check
460 ArgValues avs = avm.getOrCreateArgValues(a);
462 // store appropriate String value(s)
463 if (a.hasOption(Opt.STRING))
465 if (a.hasOption(Opt.GLOB) && globVals != null
466 && globVals.size() > 0)
468 Enumeration<String> gve = Collections.enumeration(globVals);
469 while (gve.hasMoreElements())
471 String v = gve.nextElement();
472 SubVals vsv = new SubVals(globSubVals, v);
473 addValue(linkedId, avs, vsv, v, argIndex++, true);
474 // if we're using defaultLinkedId and the arg increments the
476 if (gve.hasMoreElements() && usingDefaultLinkedId
477 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
479 // increment the default linkedId
480 linkedId = defaultLinkedId(true);
481 // get new avm and avs
482 avm = linkedArgs.get(linkedId);
483 avs = avm.getOrCreateArgValues(a);
489 addValue(linkedId, avs, val, argIndex, true);
492 else if (a.hasOption(Opt.BOOLEAN))
494 setBoolean(linkedId, avs, !negated, argIndex);
495 setNegated(linkedId, avs, negated);
497 else if (a.hasOption(Opt.UNARY))
499 setBoolean(linkedId, avs, true, argIndex);
502 // remove the '*' linkedId that should be empty if it was created
503 if (MATCHALLLINKEDIDS.equals(linkedId)
504 && linkedArgs.containsKey(linkedId))
506 linkedArgs.remove(linkedId);
512 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
515 incrementCount(linkedId, avs);
518 // store in appropriate place
519 if (a.hasOption(Opt.LINKED))
521 // store the order of linkedIds
522 if (!linkedOrder.contains(linkedId))
523 linkedOrder.add(linkedId);
526 // store arg in the list of args used
527 if (!argList.contains(a))
531 private String defaultLinkedId(boolean increment)
533 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
534 .append(Integer.toString(defaultLinkedIdCounter)).toString();
537 while (linkedArgs.containsKey(defaultLinkedId))
539 defaultLinkedIdCounter++;
540 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
541 .append(Integer.toString(defaultLinkedIdCounter))
545 getOrCreateLinkedArgValuesMap(defaultLinkedId);
546 return defaultLinkedId;
549 public String makeSubstitutions(String val, String linkedId)
551 if (!this.substitutions || val == null)
556 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
558 int closeBracket = val.indexOf(']');
559 if (val.length() == closeBracket)
561 subvals = val.substring(0, closeBracket + 1);
562 rest = val.substring(closeBracket + 1);
569 if (rest.contains(LINKEDIDAUTOCOUNTER))
570 rest = rest.replace(LINKEDIDAUTOCOUNTER,
571 String.valueOf(linkedIdAutoCounter));
572 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
573 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
574 String.valueOf(++linkedIdAutoCounter));
575 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
576 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
577 String.valueOf(defaultLinkedIdCounter));
578 ArgValuesMap avm = linkedArgs.get(linkedId);
581 if (rest.contains(LINKEDIDBASENAME))
583 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
585 if (rest.contains(LINKEDIDDIRNAME))
587 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
592 if (rest.contains(ARGFILEBASENAME))
594 rest = rest.replace(ARGFILEBASENAME,
595 FileUtils.getBasename(new File(argFile)));
597 if (rest.contains(ARGFILEDIRNAME))
599 rest = rest.replace(ARGFILEDIRNAME,
600 FileUtils.getDirname(new File(argFile)));
604 return new StringBuilder(subvals).append(rest).toString();
608 * A helper method to take a list of String args where we're expecting
609 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
610 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
611 * "file2", "file3"} *and remove these from the original list object* so that
612 * processing can continue from where it has left off, e.g. args has become
613 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
614 * carries on from the next --arg if available.
616 protected static List<String> getShellGlobbedFilenameValues(Arg a,
617 List<String> args, int i)
619 List<String> vals = new ArrayList<>();
620 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
622 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
623 if (!a.hasOption(Opt.GLOB))
629 public BootstrapArgs getBootstrapArgs()
631 return bootstrapArgs;
634 public boolean isSet(Arg a)
636 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
639 public boolean isSetAtAll(Arg a)
641 for (String linkedId : linkedOrder)
643 if (isSet(linkedId, a))
649 public boolean isSet(String linkedId, Arg a)
651 ArgValuesMap avm = linkedArgs.get(linkedId);
652 return avm == null ? false : avm.containsArg(a);
655 public boolean getBoolean(Arg a)
657 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
659 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
662 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
665 public boolean getBool(String linkedId, Arg a)
667 ArgValuesMap avm = linkedArgs.get(linkedId);
669 return a.getDefaultBoolValue();
670 ArgValues avs = avm.getArgValues(a);
671 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
674 public List<String> getLinkedIds()
679 public ArgValuesMap getLinkedArgs(String id)
681 return linkedArgs.get(id);
685 public String toString()
687 StringBuilder sb = new StringBuilder();
688 sb.append("UNLINKED\n");
689 sb.append(argValuesMapToString(linkedArgs.get(null)));
690 if (getLinkedIds() != null)
692 sb.append("LINKED\n");
693 for (String id : getLinkedIds())
695 // already listed these as UNLINKED args
699 ArgValuesMap avm = getLinkedArgs(id);
700 sb.append("ID: '").append(id).append("'\n");
701 sb.append(argValuesMapToString(avm));
704 return sb.toString();
707 private static String argValuesMapToString(ArgValuesMap avm)
711 StringBuilder sb = new StringBuilder();
712 for (Arg a : avm.getArgKeys())
714 ArgValues v = avm.getArgValues(a);
715 sb.append(v.toString());
718 return sb.toString();
721 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
722 boolean initsubstitutions, BootstrapArgs bsa)
724 List<File> argFiles = new ArrayList<>();
726 for (String pattern : argFilenameGlobs)
728 // I don't think we want to dedup files, making life easier
729 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
732 return parseArgFileList(argFiles, initsubstitutions, bsa);
735 public static ArgParser parseArgFileList(List<File> argFiles,
736 boolean initsubstitutions, BootstrapArgs bsa)
738 List<String> argsList = new ArrayList<>();
739 for (File argFile : argFiles)
741 if (!argFile.exists())
743 String message = Arg.ARGFILE.argString() + EQUALS + "\""
744 + argFile.getPath() + "\": File does not exist.";
745 Jalview.exit(message, 2);
749 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
750 .append(EQUALS).append(argFile.getCanonicalPath())
752 argsList.add(setargfile);
753 argsList.addAll(readArgFile(argFile));
754 argsList.add(Arg.UNSETARGFILE.argString());
755 } catch (IOException e)
757 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
758 + "\": File could not be read.";
759 Jalview.exit(message, 3);
762 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
764 return new ArgParser(argsList, initsubstitutions, true, bsa);
767 protected static List<String> readArgFile(File argFile)
769 List<String> args = new ArrayList<>();
770 if (argFile != null && argFile.exists())
774 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
776 if (line != null && line.length() > 0
777 && line.charAt(0) != ARGFILECOMMENT)
780 } catch (IOException e)
782 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
783 + "\": File could not be read.";
784 Console.debug(message, e);
785 Jalview.exit(message, 3);
791 public static enum Position
796 // get from following Arg of type a or subval of same name (lowercase)
797 public static String getValueFromSubValOrArg(ArgValuesMap avm,
798 ArgValue av, Arg a, SubVals sv)
800 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
803 // get from following Arg of type a or subval key or preference pref or
805 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
806 Arg a, SubVals sv, String key, String pref, String def)
808 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
812 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
813 // Arg of type a or subval key or preference pref or default def
814 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
815 Position pos, ArgValue av, SubVals sv, String key, String pref,
818 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
822 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
823 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
824 String key, String pref, String def)
829 if (sv != null && sv.has(key) && sv.get(key) != null)
831 value = ap == null ? sv.get(key)
832 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
834 else if (avm != null && avm.containsArg(a))
836 if (pos == Position.FIRST && avm.getValue(a) != null)
837 value = avm.getValue(a);
838 else if (pos == Position.BEFORE
839 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
840 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
841 else if (pos == Position.AFTER
842 && avm.getClosestNextArgValueOfArg(av, a) != null)
843 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
847 value = pref != null ? Cache.getDefault(pref, def) : def;
852 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
855 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
858 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
859 SubVals sv, String key, String pref, boolean def)
861 if ((key == null && a == null) || (sv == null && a == null))
864 boolean usingArgKey = false;
871 String nokey = ArgParser.NEGATESTRING + key;
873 // look for key or nokey in subvals first (if using Arg check options)
876 // check for true boolean
877 if (sv.has(key) && sv.get(key) != null)
881 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
884 "Looking for boolean in subval from non-boolean/non-unary Arg "
889 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
892 // check for negative boolean (subval "no..." will be "true")
893 if (sv.has(nokey) && sv.get(nokey) != null)
897 if (!(a.hasOption(Opt.BOOLEAN)))
900 "Looking for negative boolean in subval from non-boolean Arg "
905 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
910 if (avm != null && avm.containsArg(a))
911 return avm.getBoolean(a);
913 // return preference or default
914 return pref != null ? Cache.getDefault(pref, def) : def;
917 // the following methods look for the "*" linkedId and add the argvalue to all
918 // linkedId ArgValues if it does
919 private void addValue(String linkedId, ArgValues avs, SubVals sv,
920 String v, int argIndex, boolean doSubs)
923 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
925 for (String id : getLinkedIds())
927 if (id == null || MATCHALLLINKEDIDS.equals(id))
929 ArgValuesMap avm = linkedArgs.get(id);
930 if (a.hasOption(Opt.REQUIREINPUT)
931 && !avm.hasArgWithOption(Opt.INPUT))
933 ArgValues tavs = avm.getOrCreateArgValues(a);
937 val = makeSubstitutions(v, id);
938 sv = new SubVals(sv, val);
940 tavs.addValue(sv, val, argIndex);
941 finaliseStoringArgValue(id, tavs);
949 val = makeSubstitutions(v, linkedId);
950 sv = new SubVals(sv, val);
952 avs.addValue(sv, val, argIndex);
953 finaliseStoringArgValue(linkedId, avs);
957 private void addValue(String linkedId, ArgValues avs, String v,
958 int argIndex, boolean doSubs)
961 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
963 for (String id : getLinkedIds())
965 if (id == null || MATCHALLLINKEDIDS.equals(id))
967 ArgValuesMap avm = linkedArgs.get(id);
968 // don't set an output if there isn't an input
969 if (a.hasOption(Opt.REQUIREINPUT)
970 && !avm.hasArgWithOption(Opt.INPUT))
972 ArgValues tavs = avm.getOrCreateArgValues(a);
973 String val = doSubs ? makeSubstitutions(v, id) : v;
974 tavs.addValue(val, argIndex);
975 finaliseStoringArgValue(id, tavs);
980 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
981 avs.addValue(val, argIndex);
982 finaliseStoringArgValue(linkedId, avs);
986 private void setBoolean(String linkedId, ArgValues avs, boolean b,
990 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
992 for (String id : getLinkedIds())
994 if (id == null || MATCHALLLINKEDIDS.equals(id))
996 ArgValuesMap avm = linkedArgs.get(id);
997 if (a.hasOption(Opt.REQUIREINPUT)
998 && !avm.hasArgWithOption(Opt.INPUT))
1000 ArgValues tavs = avm.getOrCreateArgValues(a);
1001 tavs.setBoolean(b, argIndex);
1002 finaliseStoringArgValue(id, tavs);
1007 avs.setBoolean(b, argIndex);
1008 finaliseStoringArgValue(linkedId, avs);
1012 private void setNegated(String linkedId, ArgValues avs, boolean b)
1015 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1017 for (String id : getLinkedIds())
1019 if (id == null || MATCHALLLINKEDIDS.equals(id))
1021 ArgValuesMap avm = linkedArgs.get(id);
1022 if (a.hasOption(Opt.REQUIREINPUT)
1023 && !avm.hasArgWithOption(Opt.INPUT))
1025 ArgValues tavs = avm.getOrCreateArgValues(a);
1035 private void incrementCount(String linkedId, ArgValues avs)
1038 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
1040 for (String id : getLinkedIds())
1042 if (id == null || MATCHALLLINKEDIDS.equals(id))
1044 ArgValuesMap avm = linkedArgs.get(id);
1045 if (a.hasOption(Opt.REQUIREINPUT)
1046 && !avm.hasArgWithOption(Opt.INPUT))
1048 ArgValues tavs = avm.getOrCreateArgValues(a);
1049 tavs.incrementCount();
1054 avs.incrementCount();
1058 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1060 if (linkedArgs.containsKey(linkedId)
1061 && linkedArgs.get(linkedId) != null)
1062 return linkedArgs.get(linkedId);
1064 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1065 return linkedArgs.get(linkedId);