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 linkedId string used to match all of the last --open'ed linkedIds
59 protected static final String MATCHOPENEDLINKEDIDS = "open*";
61 // the counter added to the default linked id prefix
62 private int defaultLinkedIdCounter = 0;
64 // the substitution string used to use the defaultLinkedIdCounter
65 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
67 // the counter added to the default linked id prefix. NOW using
68 // linkedIdAutoCounter
69 // private int openLinkedIdCounter = 0;
71 // the linked id prefix used for --open files. NOW the same as DEFAULT
72 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
74 // the counter used for {n} substitutions
75 private int linkedIdAutoCounter = 0;
77 // the linked id substitution string used to increment the idCounter (and use
78 // the incremented value)
79 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
81 // the linked id substitution string used to use the idCounter
82 private static final String LINKEDIDAUTOCOUNTER = "{n}";
84 // the linked id substitution string used to use the base filename of --append
86 private static final String LINKEDIDBASENAME = "{basename}";
88 // the linked id substitution string used to use the dir path of --append
90 private static final String LINKEDIDDIRNAME = "{dirname}";
92 // the current argfile
93 private String argFile = null;
95 // the linked id substitution string used to use the dir path of the latest
97 private static final String ARGFILEBASENAME = "{argfilebasename}";
99 // the linked id substitution string used to use the dir path of the latest
101 private static final String ARGFILEDIRNAME = "{argfiledirname}";
103 // an output file wildcard to signify --output=*.ext is really --all --output
105 private static final String OUTPUTWILDCARD = "*.";
107 // flag to say whether {n} subtitutions in output filenames should be made.
108 // Turn on and off with --substitutions and --nosubstitutions
110 private boolean substitutions = true;
112 // flag to say whether the default linkedId is the current default linked id
114 private boolean allLinkedIds = false;
116 // flag to say whether the default linkedId is the current default linked id
117 // or OPENED linkedIds
118 private boolean openedLinkedIds = false;
120 protected static final Map<String, Arg> argMap;
122 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
124 protected List<String> linkedOrder = new ArrayList<>();
126 protected List<String> storedLinkedIds = new ArrayList<>();
128 protected List<Arg> argList = new ArrayList<>();
130 private static final char ARGFILECOMMENT = '#';
132 private int argIndex = 0;
134 private BootstrapArgs bootstrapArgs = null;
138 argMap = new HashMap<>();
139 for (Arg a : EnumSet.allOf(Arg.class))
141 for (String argName : a.getNames())
143 if (argMap.containsKey(argName))
145 Console.warn("Trying to add argument name multiple times: '"
146 + argName + "'"); // RESTORE THIS WHEN
148 if (argMap.get(argName) != a)
151 "Trying to add argument name multiple times for different Args: '"
152 + argMap.get(argName).getName() + ":" + argName
153 + "' and '" + a.getName() + ":" + argName
158 argMap.put(argName, a);
163 public ArgParser(String[] args)
165 this(args, false, null);
168 public ArgParser(String[] args, boolean initsubstitutions,
171 // Make a mutable new ArrayList so that shell globbing parser works.
172 // (When shell file globbing is used, there are a sequence of non-Arg
173 // arguments (which are the expanded globbed filenames) that need to be
174 // consumed by the --append/--argfile/etc Arg which is most easily done by
175 // removing these filenames from the list one at a time. This can't be done
176 // with an ArrayList made with only Arrays.asList(String[] args). )
177 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
181 public ArgParser(List<String> args, boolean initsubstitutions)
183 this(args, initsubstitutions, false, null);
186 public ArgParser(List<String> args, boolean initsubstitutions,
187 boolean allowPrivate, BootstrapArgs bsa)
189 // do nothing if there are no "--" args and (some "-" args || >0 arg is
193 for (String arg : args)
195 if (arg.startsWith(DOUBLEDASH))
200 else if (arg.startsWith("-") || arg.equals("open"))
207 // leave it to the old style -- parse an empty list
208 parse(new ArrayList<String>(), false, false);
212 this.bootstrapArgs = bsa;
214 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
215 parse(args, initsubstitutions, allowPrivate);
218 private void parse(List<String> args, boolean initsubstitutions,
219 boolean allowPrivate)
221 this.substitutions = initsubstitutions;
222 boolean openEachInitialFilenames = true;
223 for (int i = 0; i < args.size(); i++)
225 String arg = args.get(i);
227 // If the first arguments do not start with "--" or "-" or is not "open"
228 // and` is a filename that exists it is probably a file/list of files to
229 // open so we fake an Arg.OPEN argument and when adding files only add the
230 // single arg[i] and increment the defaultLinkedIdCounter so that each of
231 // these files is opened separately.
232 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
233 && !arg.startsWith("-") && !arg.equals("open")
234 && (new File(arg).exists()
235 || HttpUtils.startsWithHttpOrHttps(arg)))
237 arg = Arg.OPEN.argString();
241 openEachInitialFilenames = false;
244 String argName = null;
246 List<String> globVals = null; // for Opt.GLOB only
247 SubVals globSubVals = null; // also for use by Opt.GLOB only
248 String linkedId = null;
249 if (arg.startsWith(DOUBLEDASH))
251 int equalPos = arg.indexOf(EQUALS);
254 argName = arg.substring(DOUBLEDASH.length(), equalPos);
255 val = arg.substring(equalPos + 1);
259 argName = arg.substring(DOUBLEDASH.length());
261 int idOpen = argName.indexOf('[');
262 int idClose = argName.indexOf(']');
264 if (idOpen > -1 && idClose == argName.length() - 1)
266 linkedId = argName.substring(idOpen + 1, idClose);
267 argName = argName.substring(0, idOpen);
270 Arg a = argMap.get(argName);
271 // check for boolean prepended by "no"
272 boolean negated = false;
273 if (a == null && argName.startsWith(NEGATESTRING) && argMap
274 .containsKey(argName.substring(NEGATESTRING.length())))
276 argName = argName.substring(NEGATESTRING.length());
277 a = argMap.get(argName);
281 // check for config errors
285 Console.error("Argument '" + arg + "' not recognised. Exiting.");
286 Jalview.exit("Invalid argument used." + System.lineSeparator()
287 + "Use" + System.lineSeparator() + "jalview "
288 + Arg.HELP.argString() + System.lineSeparator()
289 + "for a usage statement.", 13);
292 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
295 "Argument '" + a.argString() + "' is private. Ignoring.");
298 if (!a.hasOption(Opt.BOOLEAN) && negated)
300 // used "no" with a non-boolean option
301 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
302 + "' not a boolean option. Ignoring.");
305 if (!a.hasOption(Opt.STRING) && equalPos > -1)
307 // set --argname=value when arg does not accept values
308 Console.error("Argument '" + a.argString()
309 + "' does not expect a value (given as '" + arg
313 if (!a.hasOption(Opt.LINKED) && linkedId != null)
315 // set --argname[linkedId] when arg does not use linkedIds
316 Console.error("Argument '" + a.argString()
317 + "' does not expect a linked id (given as '" + arg
323 if (a.hasOption(Opt.STRING))
327 if (a.hasOption(Opt.GLOB))
329 // strip off and save the SubVals to be added individually later
330 globSubVals = new SubVals(val);
331 // make substitutions before looking for files
332 String fileGlob = makeSubstitutions(globSubVals.getContent(),
334 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
338 // val is already set -- will be saved in the ArgValue later in
344 // There is no "=" so value is next arg or args (possibly shell
346 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
348 // no value to take for arg, which wants a value
349 Console.error("Argument '" + a.getName()
350 + "' requires a value, none given. Ignoring.");
353 // deal with bash globs here (--arg val* is expanded before reaching
354 // the JVM). Note that SubVals cannot be used in this case.
355 // If using the --arg=val then the glob is preserved and Java globs
356 // will be used later. SubVals can be used.
357 if (a.hasOption(Opt.GLOB))
359 // if this is the first argument with a file list at the start of
360 // the args we add filenames from index i instead of i+1
361 globVals = getShellGlobbedFilenameValues(a, args,
362 openEachInitialFilenames ? i : i + 1);
366 val = args.get(i + 1);
371 // make NOACTION adjustments
372 // default and auto counter increments
375 linkedIdAutoCounter++;
377 else if (a == Arg.SUBSTITUTIONS)
379 substitutions = !negated;
381 else if (a == Arg.SETARGFILE)
385 else if (a == Arg.UNSETARGFILE)
389 else if (a == Arg.ALL)
391 allLinkedIds = !negated;
392 openedLinkedIds = false;
394 else if (a == Arg.OPENED)
396 openedLinkedIds = !negated;
397 allLinkedIds = false;
400 if (a.hasOption(Opt.STORED))
402 // reset the lastOpenedLinkedIds list
403 this.storedLinkedIds = new ArrayList<>();
406 // this is probably only Arg.NEW and Arg.OPEN
407 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
409 // use the next default prefixed OPENLINKEDID
410 defaultLinkedId(true);
413 String autoCounterString = null;
414 boolean usingAutoCounterLinkedId = false;
415 String defaultLinkedId = defaultLinkedId(false);
416 boolean usingDefaultLinkedId = false;
417 if (a.hasOption(Opt.LINKED))
419 if (linkedId == null)
421 if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
422 && val.startsWith(MATCHALLLINKEDIDS))
424 // --output=*.ext is shorthand for --all --output {basename}.ext
425 // (or --image=*.ext)
427 openedLinkedIds = false;
428 linkedId = MATCHALLLINKEDIDS;
429 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
430 + val.substring(MATCHALLLINKEDIDS.length());
432 else if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
433 && val.startsWith(MATCHOPENEDLINKEDIDS))
435 // --output=open*.ext is shorthand for --opened --output
437 // (or --image=open*.ext)
438 openedLinkedIds = true;
439 allLinkedIds = false;
440 linkedId = MATCHOPENEDLINKEDIDS;
441 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
442 + val.substring(MATCHOPENEDLINKEDIDS.length());
444 else if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
446 linkedId = MATCHALLLINKEDIDS;
448 else if (openedLinkedIds && a.hasOption(Opt.ALLOWALL))
450 linkedId = MATCHOPENEDLINKEDIDS;
454 // use default linkedId for linked arguments
455 linkedId = defaultLinkedId;
456 usingDefaultLinkedId = true;
457 Console.debug("Changing linkedId to '" + linkedId + "' from "
461 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
463 // turn {n} to the autoCounter
464 autoCounterString = Integer.toString(linkedIdAutoCounter);
465 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
467 usingAutoCounterLinkedId = true;
469 "Changing linkedId to '" + linkedId + "' from " + arg);
471 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
473 // turn {++n} to the incremented autoCounter
474 autoCounterString = Integer.toString(++linkedIdAutoCounter);
475 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
477 usingAutoCounterLinkedId = true;
479 "Changing linkedId to '" + linkedId + "' from " + arg);
483 // do not continue in this block for NOACTION args
484 if (a.hasOption(Opt.NOACTION))
487 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
489 // not dealing with both NODUPLICATEVALUES and GLOB
490 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
492 Console.error("Argument '" + a.argString()
493 + "' cannot contain a duplicate value ('" + val
494 + "'). Ignoring this and subsequent occurrences.");
498 // check for unique id
499 SubVals idsv = new SubVals(val);
500 String id = idsv.get(ArgValues.ID);
501 if (id != null && avm.hasId(a, id))
503 Console.error("Argument '" + a.argString()
504 + "' has a duplicate id ('" + id + "'). Ignoring.");
509 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
510 * DONE, need to check
512 ArgValues avs = avm.getOrCreateArgValues(a);
514 // store appropriate String value(s)
515 if (a.hasOption(Opt.STRING))
517 if (a.hasOption(Opt.GLOB) && globVals != null
518 && globVals.size() > 0)
520 Enumeration<String> gve = Collections.enumeration(globVals);
521 while (gve.hasMoreElements())
523 String v = gve.nextElement();
524 SubVals vsv = new SubVals(globSubVals, v);
525 addValue(linkedId, avs, vsv, v, argIndex++, true);
526 // if we're using defaultLinkedId and the arg increments the
528 if (gve.hasMoreElements() && usingDefaultLinkedId
529 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
531 // increment the default linkedId
532 linkedId = defaultLinkedId(true);
533 // get new avm and avs
534 avm = linkedArgs.get(linkedId);
535 avs = avm.getOrCreateArgValues(a);
541 addValue(linkedId, avs, val, argIndex, true);
544 else if (a.hasOption(Opt.BOOLEAN))
546 setBoolean(linkedId, avs, !negated, argIndex);
547 setNegated(linkedId, avs, negated);
549 else if (a.hasOption(Opt.UNARY))
551 setBoolean(linkedId, avs, true, argIndex);
554 // remove the '*' or 'open*' linkedId that should be empty if it was
556 if ((MATCHALLLINKEDIDS.equals(linkedId)
557 && linkedArgs.containsKey(linkedId))
558 || (MATCHOPENEDLINKEDIDS.equals(linkedId)
559 && linkedArgs.containsKey(linkedId)))
561 linkedArgs.remove(linkedId);
567 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
570 incrementCount(linkedId, avs);
573 // store in appropriate place
574 if (a.hasOption(Opt.LINKED))
576 // store the order of linkedIds
577 if (!linkedOrder.contains(linkedId))
578 linkedOrder.add(linkedId);
581 // store arg in the list of args used
582 if (!argList.contains(a))
586 private String defaultLinkedId(boolean increment)
588 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
589 .append(Integer.toString(defaultLinkedIdCounter)).toString();
592 while (linkedArgs.containsKey(defaultLinkedId))
594 defaultLinkedIdCounter++;
595 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
596 .append(Integer.toString(defaultLinkedIdCounter))
600 getOrCreateLinkedArgValuesMap(defaultLinkedId);
601 return defaultLinkedId;
604 public String makeSubstitutions(String val, String linkedId)
606 if (!this.substitutions || val == null)
611 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
613 int closeBracket = val.indexOf(']');
614 if (val.length() == closeBracket)
616 subvals = val.substring(0, closeBracket + 1);
617 rest = val.substring(closeBracket + 1);
624 if (rest.contains(LINKEDIDAUTOCOUNTER))
625 rest = rest.replace(LINKEDIDAUTOCOUNTER,
626 String.valueOf(linkedIdAutoCounter));
627 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
628 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
629 String.valueOf(++linkedIdAutoCounter));
630 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
631 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
632 String.valueOf(defaultLinkedIdCounter));
633 ArgValuesMap avm = linkedArgs.get(linkedId);
636 if (rest.contains(LINKEDIDBASENAME))
638 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
640 if (rest.contains(LINKEDIDDIRNAME))
642 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
647 if (rest.contains(ARGFILEBASENAME))
649 rest = rest.replace(ARGFILEBASENAME,
650 FileUtils.getBasename(new File(argFile)));
652 if (rest.contains(ARGFILEDIRNAME))
654 rest = rest.replace(ARGFILEDIRNAME,
655 FileUtils.getDirname(new File(argFile)));
659 return new StringBuilder(subvals).append(rest).toString();
663 * A helper method to take a list of String args where we're expecting
664 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
665 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
666 * "file2", "file3"} *and remove these from the original list object* so that
667 * processing can continue from where it has left off, e.g. args has become
668 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
669 * carries on from the next --arg if available.
671 protected static List<String> getShellGlobbedFilenameValues(Arg a,
672 List<String> args, int i)
674 List<String> vals = new ArrayList<>();
675 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
677 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
678 if (!a.hasOption(Opt.GLOB))
684 public BootstrapArgs getBootstrapArgs()
686 return bootstrapArgs;
689 public boolean isSet(Arg a)
691 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
694 public boolean isSetAtAll(Arg a)
696 for (String linkedId : linkedOrder)
698 if (isSet(linkedId, a))
704 public boolean isSet(String linkedId, Arg a)
706 ArgValuesMap avm = linkedArgs.get(linkedId);
707 return avm == null ? false : avm.containsArg(a);
710 public boolean getBoolean(Arg a)
712 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
714 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
717 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
720 public boolean getBool(String linkedId, Arg a)
722 ArgValuesMap avm = linkedArgs.get(linkedId);
724 return a.getDefaultBoolValue();
725 ArgValues avs = avm.getArgValues(a);
726 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
729 public List<String> getLinkedIds()
734 public ArgValuesMap getLinkedArgs(String id)
736 return linkedArgs.get(id);
740 public String toString()
742 StringBuilder sb = new StringBuilder();
743 sb.append("UNLINKED\n");
744 sb.append(argValuesMapToString(linkedArgs.get(null)));
745 if (getLinkedIds() != null)
747 sb.append("LINKED\n");
748 for (String id : getLinkedIds())
750 // already listed these as UNLINKED args
754 ArgValuesMap avm = getLinkedArgs(id);
755 sb.append("ID: '").append(id).append("'\n");
756 sb.append(argValuesMapToString(avm));
759 return sb.toString();
762 private static String argValuesMapToString(ArgValuesMap avm)
766 StringBuilder sb = new StringBuilder();
767 for (Arg a : avm.getArgKeys())
769 ArgValues v = avm.getArgValues(a);
770 sb.append(v.toString());
773 return sb.toString();
776 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
777 boolean initsubstitutions, BootstrapArgs bsa)
779 List<File> argFiles = new ArrayList<>();
781 for (String pattern : argFilenameGlobs)
783 // I don't think we want to dedup files, making life easier
784 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
787 return parseArgFileList(argFiles, initsubstitutions, bsa);
790 public static ArgParser parseArgFileList(List<File> argFiles,
791 boolean initsubstitutions, BootstrapArgs bsa)
793 List<String> argsList = new ArrayList<>();
794 for (File argFile : argFiles)
796 if (!argFile.exists())
798 String message = Arg.ARGFILE.argString() + EQUALS + "\""
799 + argFile.getPath() + "\": File does not exist.";
800 Jalview.exit(message, 2);
804 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
805 .append(EQUALS).append(argFile.getCanonicalPath())
807 argsList.add(setargfile);
808 argsList.addAll(readArgFile(argFile));
809 argsList.add(Arg.UNSETARGFILE.argString());
810 } catch (IOException e)
812 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
813 + "\": File could not be read.";
814 Jalview.exit(message, 3);
817 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
819 return new ArgParser(argsList, initsubstitutions, true, bsa);
822 protected static List<String> readArgFile(File argFile)
824 List<String> args = new ArrayList<>();
825 if (argFile != null && argFile.exists())
829 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
831 if (line != null && line.length() > 0
832 && line.charAt(0) != ARGFILECOMMENT)
835 } catch (IOException e)
837 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
838 + "\": File could not be read.";
839 Console.debug(message, e);
840 Jalview.exit(message, 3);
846 public static enum Position
851 // get from following Arg of type a or subval of same name (lowercase)
852 public static String getValueFromSubValOrArg(ArgValuesMap avm,
853 ArgValue av, Arg a, SubVals sv)
855 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
858 // get from following Arg of type a or subval key or preference pref or
860 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
861 Arg a, SubVals sv, String key, String pref, String def)
863 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
867 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
868 // Arg of type a or subval key or preference pref or default def
869 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
870 Position pos, ArgValue av, SubVals sv, String key, String pref,
873 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
877 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
878 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
879 String key, String pref, String def)
884 if (sv != null && sv.has(key) && sv.get(key) != null)
886 value = ap == null ? sv.get(key)
887 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
889 else if (avm != null && avm.containsArg(a))
891 if (pos == Position.FIRST && avm.getValue(a) != null)
892 value = avm.getValue(a);
893 else if (pos == Position.BEFORE
894 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
895 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
896 else if (pos == Position.AFTER
897 && avm.getClosestNextArgValueOfArg(av, a) != null)
898 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
902 value = pref != null ? Cache.getDefault(pref, def) : def;
907 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
910 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
913 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
914 SubVals sv, String key, String pref, boolean def)
916 if ((key == null && a == null) || (sv == null && a == null))
919 boolean usingArgKey = false;
926 String nokey = ArgParser.NEGATESTRING + key;
928 // look for key or nokey in subvals first (if using Arg check options)
931 // check for true boolean
932 if (sv.has(key) && sv.get(key) != null)
936 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
939 "Looking for boolean in subval from non-boolean/non-unary Arg "
944 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
947 // check for negative boolean (subval "no..." will be "true")
948 if (sv.has(nokey) && sv.get(nokey) != null)
952 if (!(a.hasOption(Opt.BOOLEAN)))
955 "Looking for negative boolean in subval from non-boolean Arg "
960 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
965 if (avm != null && avm.containsArg(a))
966 return avm.getBoolean(a);
968 // return preference or default
969 return pref != null ? Cache.getDefault(pref, def) : def;
972 // the following methods look for the "*" linkedId and add the argvalue to all
973 // linkedId ArgValues if it does.
974 // This version inserts the subvals sv into all created values
975 private void addValue(String linkedId, ArgValues avs, SubVals sv,
976 String v, int argIndex, boolean doSubs)
978 this.argValueOperation(Op.ADDVALUE, linkedId, avs, sv, v, false,
982 private void NOTaddValue(String linkedId, ArgValues avs, SubVals sv,
983 String v, int argIndex, boolean doSubs)
987 List<String> wildcardLinkedIds = null;
988 if (a.hasOption(Opt.ALLOWALL))
992 case MATCHALLLINKEDIDS:
993 wildcardLinkedIds = getLinkedIds();
995 case MATCHOPENEDLINKEDIDS:
996 wildcardLinkedIds = this.storedLinkedIds;
1001 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1002 // to storedLinkedIds
1003 if (linkedId != null && wildcardLinkedIds == null
1004 && a.hasOption(Opt.STORED)
1005 && !storedLinkedIds.contains(linkedId))
1007 storedLinkedIds.add(linkedId);
1010 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1012 if (wildcardLinkedIds != null)
1014 for (String id : wildcardLinkedIds)
1016 // skip incorrectly stored wildcard ids!
1017 if (id == null || MATCHALLLINKEDIDS.equals(id)
1018 || MATCHOPENEDLINKEDIDS.equals(id))
1020 ArgValuesMap avm = linkedArgs.get(id);
1021 if (a.hasOption(Opt.REQUIREINPUT)
1022 && !avm.hasArgWithOption(Opt.INPUT))
1024 ArgValues tavs = avm.getOrCreateArgValues(a);
1028 val = makeSubstitutions(v, id);
1029 sv = new SubVals(sv, val);
1031 tavs.addValue(sv, val, argIndex);
1032 finaliseStoringArgValue(id, tavs);
1040 val = makeSubstitutions(v, linkedId);
1041 sv = new SubVals(sv, val);
1043 avs.addValue(sv, val, argIndex);
1044 finaliseStoringArgValue(linkedId, avs);
1048 private void addValue(String linkedId, ArgValues avs, String v,
1049 int argIndex, boolean doSubs)
1051 this.argValueOperation(Op.ADDVALUE, linkedId, avs, null, v, false,
1055 // the following methods look for the "*" linkedId and add the argvalue to all
1056 // linkedId ArgValues if it does.
1057 private void NOTaddValue(String linkedId, ArgValues avs, String v,
1058 int argIndex, boolean doSubs)
1061 if (linkedId != null && a.hasOption(Opt.STORED)
1062 && !storedLinkedIds.contains(linkedId))
1064 storedLinkedIds.add(linkedId);
1067 List<String> wildcardLinkedIds = null;
1068 if (a.hasOption(Opt.ALLOWALL))
1072 case MATCHALLLINKEDIDS:
1073 wildcardLinkedIds = getLinkedIds();
1075 case MATCHOPENEDLINKEDIDS:
1076 wildcardLinkedIds = this.storedLinkedIds;
1081 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1082 // to storedLinkedIds
1083 if (linkedId != null && wildcardLinkedIds == null
1084 && a.hasOption(Opt.STORED)
1085 && !storedLinkedIds.contains(linkedId))
1087 storedLinkedIds.add(linkedId);
1090 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1092 if (wildcardLinkedIds != null)
1094 for (String id : wildcardLinkedIds)
1096 // skip incorrectly stored wildcard ids!
1097 if (id == null || MATCHALLLINKEDIDS.equals(id)
1098 || MATCHOPENEDLINKEDIDS.equals(id))
1100 ArgValuesMap avm = linkedArgs.get(id);
1101 // don't set an output if there isn't an input
1102 if (a.hasOption(Opt.REQUIREINPUT)
1103 && !avm.hasArgWithOption(Opt.INPUT))
1105 ArgValues tavs = avm.getOrCreateArgValues(a);
1106 String val = doSubs ? makeSubstitutions(v, id) : v;
1107 tavs.addValue(val, argIndex);
1108 finaliseStoringArgValue(id, tavs);
1113 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
1114 avs.addValue(val, argIndex);
1115 finaliseStoringArgValue(linkedId, avs);
1119 private void setBoolean(String linkedId, ArgValues avs, boolean b,
1122 this.argValueOperation(Op.SETBOOLEAN, linkedId, avs, null, null, b,
1126 private void NOTsetBoolean(String linkedId, ArgValues avs, boolean b,
1130 if (linkedId != null && a.hasOption(Opt.STORED)
1131 && !storedLinkedIds.contains(linkedId))
1133 storedLinkedIds.add(linkedId);
1136 List<String> wildcardLinkedIds = null;
1137 if (a.hasOption(Opt.ALLOWALL))
1141 case MATCHALLLINKEDIDS:
1142 wildcardLinkedIds = getLinkedIds();
1144 case MATCHOPENEDLINKEDIDS:
1145 wildcardLinkedIds = this.storedLinkedIds;
1150 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1151 // to storedLinkedIds
1152 if (linkedId != null && wildcardLinkedIds == null
1153 && a.hasOption(Opt.STORED)
1154 && !storedLinkedIds.contains(linkedId))
1156 storedLinkedIds.add(linkedId);
1159 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1161 if (wildcardLinkedIds != null)
1163 for (String id : wildcardLinkedIds)
1165 // skip incorrectly stored wildcard ids!
1166 if (id == null || MATCHALLLINKEDIDS.equals(id)
1167 || MATCHOPENEDLINKEDIDS.equals(id))
1169 ArgValuesMap avm = linkedArgs.get(id);
1170 if (a.hasOption(Opt.REQUIREINPUT)
1171 && !avm.hasArgWithOption(Opt.INPUT))
1173 ArgValues tavs = avm.getOrCreateArgValues(a);
1174 tavs.setBoolean(b, argIndex);
1175 finaliseStoringArgValue(id, tavs);
1180 avs.setBoolean(b, argIndex);
1181 finaliseStoringArgValue(linkedId, avs);
1185 private void setNegated(String linkedId, ArgValues avs, boolean b)
1187 this.argValueOperation(Op.SETNEGATED, linkedId, avs, null, null, b, 0,
1191 private void NOTsetNegated(String linkedId, ArgValues avs, boolean b)
1194 if (linkedId != null && a.hasOption(Opt.STORED)
1195 && !storedLinkedIds.contains(linkedId))
1197 storedLinkedIds.add(linkedId);
1200 List<String> wildcardLinkedIds = null;
1201 if (a.hasOption(Opt.ALLOWALL))
1205 case MATCHALLLINKEDIDS:
1206 wildcardLinkedIds = getLinkedIds();
1208 case MATCHOPENEDLINKEDIDS:
1209 wildcardLinkedIds = this.storedLinkedIds;
1214 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1215 // to storedLinkedIds
1216 if (linkedId != null && wildcardLinkedIds == null
1217 && a.hasOption(Opt.STORED)
1218 && !storedLinkedIds.contains(linkedId))
1220 storedLinkedIds.add(linkedId);
1223 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1225 if (wildcardLinkedIds != null)
1227 for (String id : wildcardLinkedIds)
1229 // skip incorrectly stored wildcard ids!
1230 if (id == null || MATCHALLLINKEDIDS.equals(id)
1231 || MATCHOPENEDLINKEDIDS.equals(id))
1233 ArgValuesMap avm = linkedArgs.get(id);
1234 if (a.hasOption(Opt.REQUIREINPUT)
1235 && !avm.hasArgWithOption(Opt.INPUT))
1237 ArgValues tavs = avm.getOrCreateArgValues(a);
1247 private void incrementCount(String linkedId, ArgValues avs)
1249 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, avs, null, null,
1253 private void NOTincrementCount(String linkedId, ArgValues avs)
1257 List<String> wildcardLinkedIds = null;
1258 if (a.hasOption(Opt.ALLOWALL))
1262 case MATCHALLLINKEDIDS:
1263 wildcardLinkedIds = getLinkedIds();
1265 case MATCHOPENEDLINKEDIDS:
1266 wildcardLinkedIds = this.storedLinkedIds;
1271 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1272 // to storedLinkedIds
1273 if (linkedId != null && wildcardLinkedIds == null
1274 && a.hasOption(Opt.STORED)
1275 && !storedLinkedIds.contains(linkedId))
1277 storedLinkedIds.add(linkedId);
1280 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1282 if (wildcardLinkedIds != null)
1284 for (String id : wildcardLinkedIds)
1286 // skip incorrectly stored wildcard ids!
1287 if (id == null || MATCHALLLINKEDIDS.equals(id)
1288 || MATCHOPENEDLINKEDIDS.equals(id))
1290 ArgValuesMap avm = linkedArgs.get(id);
1291 if (a.hasOption(Opt.REQUIREINPUT)
1292 && !avm.hasArgWithOption(Opt.INPUT))
1294 ArgValues tavs = avm.getOrCreateArgValues(a);
1295 tavs.incrementCount();
1300 avs.incrementCount();
1306 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1309 // The following operations look for the "*" and "open*" linkedIds and add the
1310 // argvalue to all appropriate linkedId ArgValues if it does.
1311 // If subvals are supplied, they are inserted into all new set values.
1312 private void argValueOperation(Op op, String linkedId, ArgValues avs,
1313 SubVals sv, String v, boolean b, int argIndex, boolean doSubs)
1317 List<String> wildcardLinkedIds = null;
1318 if (a.hasOption(Opt.ALLOWALL))
1322 case MATCHALLLINKEDIDS:
1323 wildcardLinkedIds = getLinkedIds();
1325 case MATCHOPENEDLINKEDIDS:
1326 wildcardLinkedIds = this.storedLinkedIds;
1331 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1332 // to storedLinkedIds
1333 if (linkedId != null && wildcardLinkedIds == null
1334 && a.hasOption(Opt.STORED)
1335 && !storedLinkedIds.contains(linkedId))
1337 storedLinkedIds.add(linkedId);
1340 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1342 if (wildcardLinkedIds != null)
1344 for (String id : wildcardLinkedIds)
1346 // skip incorrectly stored wildcard ids!
1347 if (id == null || MATCHALLLINKEDIDS.equals(id)
1348 || MATCHOPENEDLINKEDIDS.equals(id))
1350 ArgValuesMap avm = linkedArgs.get(id);
1351 // don't set an output if there isn't an input
1352 if (a.hasOption(Opt.REQUIREINPUT)
1353 && !avm.hasArgWithOption(Opt.INPUT))
1356 ArgValues tavs = avm.getOrCreateArgValues(a);
1366 val = makeSubstitutions(v, id);
1367 sv = new SubVals(sv, val);
1369 tavs.addValue(sv, val, argIndex);
1375 val = makeSubstitutions(v, id);
1377 tavs.addValue(val, argIndex);
1379 finaliseStoringArgValue(id, tavs);
1383 tavs.setBoolean(b, argIndex);
1384 finaliseStoringArgValue(id, tavs);
1391 case INCREMENTCOUNT:
1392 tavs.incrementCount();
1402 else // no wildcard linkedId -- do it simpler
1412 val = makeSubstitutions(v, linkedId);
1413 sv = new SubVals(sv, val);
1415 avs.addValue(sv, val, argIndex);
1421 val = makeSubstitutions(v, linkedId);
1423 avs.addValue(val, argIndex);
1425 finaliseStoringArgValue(linkedId, avs);
1429 avs.setBoolean(b, argIndex);
1430 finaliseStoringArgValue(linkedId, avs);
1437 case INCREMENTCOUNT:
1438 avs.incrementCount();
1447 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1449 if (linkedArgs.containsKey(linkedId)
1450 && linkedArgs.get(linkedId) != null)
1451 return linkedArgs.get(linkedId);
1453 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1454 return linkedArgs.get(linkedId);