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 filename extension of
87 private static final String LINKEDIDEXTENSION = "{extension}";
89 // the linked id substitution string used to use the base filename of --append
91 private static final String LINKEDIDBASENAME = "{basename}";
93 // the linked id substitution string used to use the dir path of --append
95 private static final String LINKEDIDDIRNAME = "{dirname}";
97 // the current argfile
98 private String argFile = null;
100 // the linked id substitution string used to use the dir path of the latest
102 private static final String ARGFILEBASENAME = "{argfilebasename}";
104 // the linked id substitution string used to use the dir path of the latest
106 private static final String ARGFILEDIRNAME = "{argfiledirname}";
108 // an output file wildcard to signify --output=*.ext is really --all --output
110 private static final String OUTPUTWILDCARD = "*.";
112 // flag to say whether {n} subtitutions in output filenames should be made.
113 // Turn on and off with --substitutions and --nosubstitutions
115 private boolean substitutions = true;
117 // flag to say whether the default linkedId is the current default linked id
119 private boolean allLinkedIds = false;
121 // flag to say whether the default linkedId is the current default linked id
122 // or OPENED linkedIds
123 private boolean openedLinkedIds = false;
125 protected static final Map<String, Arg> argMap;
127 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
129 protected List<String> linkedOrder = new ArrayList<>();
131 protected List<String> storedLinkedIds = new ArrayList<>();
133 protected List<Arg> argList = new ArrayList<>();
135 private static final char ARGFILECOMMENT = '#';
137 private int argIndex = 0;
139 private BootstrapArgs bootstrapArgs = null;
143 argMap = new HashMap<>();
144 for (Arg a : EnumSet.allOf(Arg.class))
146 for (String argName : a.getNames())
148 if (argMap.containsKey(argName))
150 Console.warn("Trying to add argument name multiple times: '"
151 + argName + "'"); // RESTORE THIS WHEN
153 if (argMap.get(argName) != a)
156 "Trying to add argument name multiple times for different Args: '"
157 + argMap.get(argName).getName() + ":" + argName
158 + "' and '" + a.getName() + ":" + argName
163 argMap.put(argName, a);
168 public ArgParser(String[] args)
170 this(args, false, null);
173 public ArgParser(String[] args, boolean initsubstitutions,
176 // Make a mutable new ArrayList so that shell globbing parser works.
177 // (When shell file globbing is used, there are a sequence of non-Arg
178 // arguments (which are the expanded globbed filenames) that need to be
179 // consumed by the --append/--argfile/etc Arg which is most easily done by
180 // removing these filenames from the list one at a time. This can't be done
181 // with an ArrayList made with only Arrays.asList(String[] args). )
182 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
186 public ArgParser(List<String> args, boolean initsubstitutions)
188 this(args, initsubstitutions, false, null);
191 public ArgParser(List<String> args, boolean initsubstitutions,
192 boolean allowPrivate, BootstrapArgs bsa)
194 // do nothing if there are no "--" args and (some "-" args || >0 arg is
198 for (String arg : args)
200 if (arg.startsWith(DOUBLEDASH))
205 else if (arg.startsWith("-") || arg.equals("open"))
212 // leave it to the old style -- parse an empty list
213 parse(new ArrayList<String>(), false, false);
217 this.bootstrapArgs = bsa;
219 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
220 parse(args, initsubstitutions, allowPrivate);
223 private void parse(List<String> args, boolean initsubstitutions,
224 boolean allowPrivate)
226 this.substitutions = initsubstitutions;
227 boolean openEachInitialFilenames = true;
228 for (int i = 0; i < args.size(); i++)
230 String arg = args.get(i);
232 // If the first arguments do not start with "--" or "-" or is not "open"
233 // and` is a filename that exists it is probably a file/list of files to
234 // open so we fake an Arg.OPEN argument and when adding files only add the
235 // single arg[i] and increment the defaultLinkedIdCounter so that each of
236 // these files is opened separately.
237 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
238 && !arg.startsWith("-") && !arg.equals("open")
239 && (new File(arg).exists()
240 || HttpUtils.startsWithHttpOrHttps(arg)))
242 arg = Arg.OPEN.argString();
246 openEachInitialFilenames = false;
249 String argName = null;
251 List<String> globVals = null; // for Opt.GLOB only
252 SubVals globSubVals = null; // also for use by Opt.GLOB only
253 String linkedId = null;
254 if (arg.startsWith(DOUBLEDASH))
256 int equalPos = arg.indexOf(EQUALS);
259 argName = arg.substring(DOUBLEDASH.length(), equalPos);
260 val = arg.substring(equalPos + 1);
264 argName = arg.substring(DOUBLEDASH.length());
266 int idOpen = argName.indexOf('[');
267 int idClose = argName.indexOf(']');
269 if (idOpen > -1 && idClose == argName.length() - 1)
271 linkedId = argName.substring(idOpen + 1, idClose);
272 argName = argName.substring(0, idOpen);
275 Arg a = argMap.get(argName);
276 // check for boolean prepended by "no"
277 boolean negated = false;
278 if (a == null && argName.startsWith(NEGATESTRING) && argMap
279 .containsKey(argName.substring(NEGATESTRING.length())))
281 argName = argName.substring(NEGATESTRING.length());
282 a = argMap.get(argName);
286 // check for config errors
290 Console.error("Argument '" + arg + "' not recognised. Exiting.");
291 Jalview.exit("Invalid argument used." + System.lineSeparator()
292 + "Use" + System.lineSeparator() + "jalview "
293 + Arg.HELP.argString() + System.lineSeparator()
294 + "for a usage statement.", 13);
297 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
300 "Argument '" + a.argString() + "' is private. Ignoring.");
303 if (!a.hasOption(Opt.BOOLEAN) && negated)
305 // used "no" with a non-boolean option
306 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
307 + "' not a boolean option. Ignoring.");
310 if (!a.hasOption(Opt.STRING) && equalPos > -1)
312 // set --argname=value when arg does not accept values
313 Console.error("Argument '" + a.argString()
314 + "' does not expect a value (given as '" + arg
318 if (!a.hasOption(Opt.LINKED) && linkedId != null)
320 // set --argname[linkedId] when arg does not use linkedIds
321 Console.error("Argument '" + a.argString()
322 + "' does not expect a linked id (given as '" + arg
328 if (a.hasOption(Opt.STRING))
332 if (a.hasOption(Opt.GLOB))
334 // strip off and save the SubVals to be added individually later
335 globSubVals = new SubVals(val);
336 // make substitutions before looking for files
337 String fileGlob = makeSubstitutions(globSubVals.getContent(),
339 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
343 // val is already set -- will be saved in the ArgValue later in
349 // There is no "=" so value is next arg or args (possibly shell
351 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
353 // no value to take for arg, which wants a value
354 Console.error("Argument '" + a.getName()
355 + "' requires a value, none given. Ignoring.");
358 // deal with bash globs here (--arg val* is expanded before reaching
359 // the JVM). Note that SubVals cannot be used in this case.
360 // If using the --arg=val then the glob is preserved and Java globs
361 // will be used later. SubVals can be used.
362 if (a.hasOption(Opt.GLOB))
364 // if this is the first argument with a file list at the start of
365 // the args we add filenames from index i instead of i+1
366 globVals = getShellGlobbedFilenameValues(a, args,
367 openEachInitialFilenames ? i : i + 1);
371 val = args.get(i + 1);
376 // make NOACTION adjustments
377 // default and auto counter increments
380 linkedIdAutoCounter++;
382 else if (a == Arg.SUBSTITUTIONS)
384 substitutions = !negated;
386 else if (a == Arg.SETARGFILE)
390 else if (a == Arg.UNSETARGFILE)
394 else if (a == Arg.ALL)
396 allLinkedIds = !negated;
397 openedLinkedIds = false;
399 else if (a == Arg.OPENED)
401 openedLinkedIds = !negated;
402 allLinkedIds = false;
405 if (a.hasOption(Opt.STORED))
407 // reset the lastOpenedLinkedIds list
408 this.storedLinkedIds = new ArrayList<>();
411 // this is probably only Arg.NEW and Arg.OPEN
412 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
414 // use the next default prefixed OPENLINKEDID
415 defaultLinkedId(true);
418 String autoCounterString = null;
419 boolean usingAutoCounterLinkedId = false;
420 String defaultLinkedId = defaultLinkedId(false);
421 boolean usingDefaultLinkedId = false;
422 if (a.hasOption(Opt.LINKED))
424 if (linkedId == null)
426 if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
427 && val.startsWith(MATCHALLLINKEDIDS))
429 // --output=*.ext is shorthand for --all --output {basename}.ext
430 // (or --image=*.ext)
432 openedLinkedIds = false;
433 linkedId = MATCHALLLINKEDIDS;
434 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
435 + val.substring(MATCHALLLINKEDIDS.length());
437 else if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
438 && val.startsWith(MATCHOPENEDLINKEDIDS))
440 // --output=open*.ext is shorthand for --opened --output
442 // (or --image=open*.ext)
443 openedLinkedIds = true;
444 allLinkedIds = false;
445 linkedId = MATCHOPENEDLINKEDIDS;
446 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
447 + val.substring(MATCHOPENEDLINKEDIDS.length());
449 else if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
451 linkedId = MATCHALLLINKEDIDS;
453 else if (openedLinkedIds && a.hasOption(Opt.ALLOWALL))
455 linkedId = MATCHOPENEDLINKEDIDS;
459 // use default linkedId for linked arguments
460 linkedId = defaultLinkedId;
461 usingDefaultLinkedId = true;
462 Console.debug("Changing linkedId to '" + linkedId + "' from "
466 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
468 // turn {n} to the autoCounter
469 autoCounterString = Integer.toString(linkedIdAutoCounter);
470 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
472 usingAutoCounterLinkedId = true;
474 "Changing linkedId to '" + linkedId + "' from " + arg);
476 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
478 // turn {++n} to the incremented autoCounter
479 autoCounterString = Integer.toString(++linkedIdAutoCounter);
480 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
482 usingAutoCounterLinkedId = true;
484 "Changing linkedId to '" + linkedId + "' from " + arg);
488 // do not continue in this block for NOACTION args
489 if (a.hasOption(Opt.NOACTION))
492 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
494 // not dealing with both NODUPLICATEVALUES and GLOB
495 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
497 Console.error("Argument '" + a.argString()
498 + "' cannot contain a duplicate value ('" + val
499 + "'). Ignoring this and subsequent occurrences.");
503 // check for unique id
504 SubVals idsv = new SubVals(val);
505 String id = idsv.get(ArgValues.ID);
506 if (id != null && avm.hasId(a, id))
508 Console.error("Argument '" + a.argString()
509 + "' has a duplicate id ('" + id + "'). Ignoring.");
514 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
515 * DONE, need to check
517 ArgValues avs = avm.getOrCreateArgValues(a);
519 // store appropriate String value(s)
520 if (a.hasOption(Opt.STRING))
522 if (a.hasOption(Opt.GLOB) && globVals != null
523 && globVals.size() > 0)
525 Enumeration<String> gve = Collections.enumeration(globVals);
526 while (gve.hasMoreElements())
528 String v = gve.nextElement();
529 SubVals vsv = new SubVals(globSubVals, v);
530 addValue(linkedId, avs, vsv, v, argIndex++, true);
531 // if we're using defaultLinkedId and the arg increments the
533 if (gve.hasMoreElements() && usingDefaultLinkedId
534 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
536 // increment the default linkedId
537 linkedId = defaultLinkedId(true);
538 // get new avm and avs
539 avm = linkedArgs.get(linkedId);
540 avs = avm.getOrCreateArgValues(a);
546 addValue(linkedId, avs, val, argIndex, true);
549 else if (a.hasOption(Opt.BOOLEAN))
551 setBoolean(linkedId, avs, !negated, argIndex);
552 setNegated(linkedId, avs, negated);
554 else if (a.hasOption(Opt.UNARY))
556 setBoolean(linkedId, avs, true, argIndex);
559 // remove the '*' or 'open*' linkedId that should be empty if it was
561 if ((MATCHALLLINKEDIDS.equals(linkedId)
562 && linkedArgs.containsKey(linkedId))
563 || (MATCHOPENEDLINKEDIDS.equals(linkedId)
564 && linkedArgs.containsKey(linkedId)))
566 linkedArgs.remove(linkedId);
572 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
575 incrementCount(linkedId, avs);
578 // store in appropriate place
579 if (a.hasOption(Opt.LINKED))
581 // store the order of linkedIds
582 if (!linkedOrder.contains(linkedId))
583 linkedOrder.add(linkedId);
586 // store arg in the list of args used
587 if (!argList.contains(a))
591 private String defaultLinkedId(boolean increment)
593 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
594 .append(Integer.toString(defaultLinkedIdCounter)).toString();
597 while (linkedArgs.containsKey(defaultLinkedId))
599 defaultLinkedIdCounter++;
600 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
601 .append(Integer.toString(defaultLinkedIdCounter))
605 getOrCreateLinkedArgValuesMap(defaultLinkedId);
606 return defaultLinkedId;
609 public String makeSubstitutions(String val, String linkedId)
611 if (!this.substitutions || val == null)
616 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
618 int closeBracket = val.indexOf(']');
619 if (val.length() == closeBracket)
621 subvals = val.substring(0, closeBracket + 1);
622 rest = val.substring(closeBracket + 1);
629 if (rest.contains(LINKEDIDAUTOCOUNTER))
630 rest = rest.replace(LINKEDIDAUTOCOUNTER,
631 String.valueOf(linkedIdAutoCounter));
632 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
633 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
634 String.valueOf(++linkedIdAutoCounter));
635 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
636 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
637 String.valueOf(defaultLinkedIdCounter));
638 ArgValuesMap avm = linkedArgs.get(linkedId);
641 if (rest.contains(LINKEDIDBASENAME))
643 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
645 if (rest.contains(LINKEDIDEXTENSION))
647 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
649 if (rest.contains(LINKEDIDDIRNAME))
651 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
656 if (rest.contains(ARGFILEBASENAME))
658 rest = rest.replace(ARGFILEBASENAME,
659 FileUtils.getBasename(new File(argFile)));
661 if (rest.contains(ARGFILEDIRNAME))
663 rest = rest.replace(ARGFILEDIRNAME,
664 FileUtils.getDirname(new File(argFile)));
668 return new StringBuilder(subvals).append(rest).toString();
672 * A helper method to take a list of String args where we're expecting
673 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
674 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
675 * "file2", "file3"} *and remove these from the original list object* so that
676 * processing can continue from where it has left off, e.g. args has become
677 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
678 * carries on from the next --arg if available.
680 protected static List<String> getShellGlobbedFilenameValues(Arg a,
681 List<String> args, int i)
683 List<String> vals = new ArrayList<>();
684 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
686 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
687 if (!a.hasOption(Opt.GLOB))
693 public BootstrapArgs getBootstrapArgs()
695 return bootstrapArgs;
698 public boolean isSet(Arg a)
700 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
703 public boolean isSetAtAll(Arg a)
705 for (String linkedId : linkedOrder)
707 if (isSet(linkedId, a))
713 public boolean isSet(String linkedId, Arg a)
715 ArgValuesMap avm = linkedArgs.get(linkedId);
716 return avm == null ? false : avm.containsArg(a);
719 public boolean getBoolean(Arg a)
721 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
723 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
726 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
729 public boolean getBool(String linkedId, Arg a)
731 ArgValuesMap avm = linkedArgs.get(linkedId);
733 return a.getDefaultBoolValue();
734 ArgValues avs = avm.getArgValues(a);
735 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
738 public List<String> getLinkedIds()
743 public ArgValuesMap getLinkedArgs(String id)
745 return linkedArgs.get(id);
749 public String toString()
751 StringBuilder sb = new StringBuilder();
752 sb.append("UNLINKED\n");
753 sb.append(argValuesMapToString(linkedArgs.get(null)));
754 if (getLinkedIds() != null)
756 sb.append("LINKED\n");
757 for (String id : getLinkedIds())
759 // already listed these as UNLINKED args
763 ArgValuesMap avm = getLinkedArgs(id);
764 sb.append("ID: '").append(id).append("'\n");
765 sb.append(argValuesMapToString(avm));
768 return sb.toString();
771 private static String argValuesMapToString(ArgValuesMap avm)
775 StringBuilder sb = new StringBuilder();
776 for (Arg a : avm.getArgKeys())
778 ArgValues v = avm.getArgValues(a);
779 sb.append(v.toString());
782 return sb.toString();
785 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
786 boolean initsubstitutions, BootstrapArgs bsa)
788 List<File> argFiles = new ArrayList<>();
790 for (String pattern : argFilenameGlobs)
792 // I don't think we want to dedup files, making life easier
793 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
796 return parseArgFileList(argFiles, initsubstitutions, bsa);
799 public static ArgParser parseArgFileList(List<File> argFiles,
800 boolean initsubstitutions, BootstrapArgs bsa)
802 List<String> argsList = new ArrayList<>();
803 for (File argFile : argFiles)
805 if (!argFile.exists())
807 String message = Arg.ARGFILE.argString() + EQUALS + "\""
808 + argFile.getPath() + "\": File does not exist.";
809 Jalview.exit(message, 2);
813 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
814 .append(EQUALS).append(argFile.getCanonicalPath())
816 argsList.add(setargfile);
817 argsList.addAll(readArgFile(argFile));
818 argsList.add(Arg.UNSETARGFILE.argString());
819 } catch (IOException e)
821 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
822 + "\": File could not be read.";
823 Jalview.exit(message, 3);
826 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
828 return new ArgParser(argsList, initsubstitutions, true, bsa);
831 protected static List<String> readArgFile(File argFile)
833 List<String> args = new ArrayList<>();
834 if (argFile != null && argFile.exists())
838 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
840 if (line != null && line.length() > 0
841 && line.charAt(0) != ARGFILECOMMENT)
844 } catch (IOException e)
846 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
847 + "\": File could not be read.";
848 Console.debug(message, e);
849 Jalview.exit(message, 3);
855 public static enum Position
860 // get from following Arg of type a or subval of same name (lowercase)
861 public static String getValueFromSubValOrArg(ArgValuesMap avm,
862 ArgValue av, Arg a, SubVals sv)
864 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
867 // get from following Arg of type a or subval key or preference pref or
869 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
870 Arg a, SubVals sv, String key, String pref, String def)
872 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
876 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
877 // Arg of type a or subval key or preference pref or default def
878 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
879 Position pos, ArgValue av, SubVals sv, String key, String pref,
882 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
886 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
887 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
888 String key, String pref, String def)
893 if (sv != null && sv.has(key) && sv.get(key) != null)
895 value = ap == null ? sv.get(key)
896 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
898 else if (avm != null && avm.containsArg(a))
900 if (pos == Position.FIRST && avm.getValue(a) != null)
901 value = avm.getValue(a);
902 else if (pos == Position.BEFORE
903 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
904 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
905 else if (pos == Position.AFTER
906 && avm.getClosestNextArgValueOfArg(av, a) != null)
907 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
911 value = pref != null ? Cache.getDefault(pref, def) : def;
916 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
919 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
922 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
923 SubVals sv, String key, String pref, boolean def)
925 if ((key == null && a == null) || (sv == null && a == null))
928 boolean usingArgKey = false;
935 String nokey = ArgParser.NEGATESTRING + key;
937 // look for key or nokey in subvals first (if using Arg check options)
940 // check for true boolean
941 if (sv.has(key) && sv.get(key) != null)
945 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
948 "Looking for boolean in subval from non-boolean/non-unary Arg "
953 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
956 // check for negative boolean (subval "no..." will be "true")
957 if (sv.has(nokey) && sv.get(nokey) != null)
961 if (!(a.hasOption(Opt.BOOLEAN)))
964 "Looking for negative boolean in subval from non-boolean Arg "
969 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
974 if (avm != null && avm.containsArg(a))
975 return avm.getBoolean(a);
977 // return preference or default
978 return pref != null ? Cache.getDefault(pref, def) : def;
981 // the following methods look for the "*" linkedId and add the argvalue to all
982 // linkedId ArgValues if it does.
983 // This version inserts the subvals sv into all created values
984 private void addValue(String linkedId, ArgValues avs, SubVals sv,
985 String v, int argIndex, boolean doSubs)
987 this.argValueOperation(Op.ADDVALUE, linkedId, avs, sv, v, false,
991 private void NOTaddValue(String linkedId, ArgValues avs, SubVals sv,
992 String v, int argIndex, boolean doSubs)
996 List<String> wildcardLinkedIds = null;
997 if (a.hasOption(Opt.ALLOWALL))
1001 case MATCHALLLINKEDIDS:
1002 wildcardLinkedIds = getLinkedIds();
1004 case MATCHOPENEDLINKEDIDS:
1005 wildcardLinkedIds = this.storedLinkedIds;
1010 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1011 // to storedLinkedIds
1012 if (linkedId != null && wildcardLinkedIds == null
1013 && a.hasOption(Opt.STORED)
1014 && !storedLinkedIds.contains(linkedId))
1016 storedLinkedIds.add(linkedId);
1019 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1021 if (wildcardLinkedIds != null)
1023 for (String id : wildcardLinkedIds)
1025 // skip incorrectly stored wildcard ids!
1026 if (id == null || MATCHALLLINKEDIDS.equals(id)
1027 || MATCHOPENEDLINKEDIDS.equals(id))
1029 ArgValuesMap avm = linkedArgs.get(id);
1030 if (a.hasOption(Opt.REQUIREINPUT)
1031 && !avm.hasArgWithOption(Opt.INPUT))
1033 ArgValues tavs = avm.getOrCreateArgValues(a);
1037 val = makeSubstitutions(v, id);
1038 sv = new SubVals(sv, val);
1040 tavs.addValue(sv, val, argIndex);
1041 finaliseStoringArgValue(id, tavs);
1049 val = makeSubstitutions(v, linkedId);
1050 sv = new SubVals(sv, val);
1052 avs.addValue(sv, val, argIndex);
1053 finaliseStoringArgValue(linkedId, avs);
1057 private void addValue(String linkedId, ArgValues avs, String v,
1058 int argIndex, boolean doSubs)
1060 this.argValueOperation(Op.ADDVALUE, linkedId, avs, null, v, false,
1064 // the following methods look for the "*" linkedId and add the argvalue to all
1065 // linkedId ArgValues if it does.
1066 private void NOTaddValue(String linkedId, ArgValues avs, String v,
1067 int argIndex, boolean doSubs)
1070 if (linkedId != null && a.hasOption(Opt.STORED)
1071 && !storedLinkedIds.contains(linkedId))
1073 storedLinkedIds.add(linkedId);
1076 List<String> wildcardLinkedIds = null;
1077 if (a.hasOption(Opt.ALLOWALL))
1081 case MATCHALLLINKEDIDS:
1082 wildcardLinkedIds = getLinkedIds();
1084 case MATCHOPENEDLINKEDIDS:
1085 wildcardLinkedIds = this.storedLinkedIds;
1090 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1091 // to storedLinkedIds
1092 if (linkedId != null && wildcardLinkedIds == null
1093 && a.hasOption(Opt.STORED)
1094 && !storedLinkedIds.contains(linkedId))
1096 storedLinkedIds.add(linkedId);
1099 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1101 if (wildcardLinkedIds != null)
1103 for (String id : wildcardLinkedIds)
1105 // skip incorrectly stored wildcard ids!
1106 if (id == null || MATCHALLLINKEDIDS.equals(id)
1107 || MATCHOPENEDLINKEDIDS.equals(id))
1109 ArgValuesMap avm = linkedArgs.get(id);
1110 // don't set an output if there isn't an input
1111 if (a.hasOption(Opt.REQUIREINPUT)
1112 && !avm.hasArgWithOption(Opt.INPUT))
1114 ArgValues tavs = avm.getOrCreateArgValues(a);
1115 String val = doSubs ? makeSubstitutions(v, id) : v;
1116 tavs.addValue(val, argIndex);
1117 finaliseStoringArgValue(id, tavs);
1122 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
1123 avs.addValue(val, argIndex);
1124 finaliseStoringArgValue(linkedId, avs);
1128 private void setBoolean(String linkedId, ArgValues avs, boolean b,
1131 this.argValueOperation(Op.SETBOOLEAN, linkedId, avs, null, null, b,
1135 private void NOTsetBoolean(String linkedId, ArgValues avs, boolean b,
1139 if (linkedId != null && a.hasOption(Opt.STORED)
1140 && !storedLinkedIds.contains(linkedId))
1142 storedLinkedIds.add(linkedId);
1145 List<String> wildcardLinkedIds = null;
1146 if (a.hasOption(Opt.ALLOWALL))
1150 case MATCHALLLINKEDIDS:
1151 wildcardLinkedIds = getLinkedIds();
1153 case MATCHOPENEDLINKEDIDS:
1154 wildcardLinkedIds = this.storedLinkedIds;
1159 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1160 // to storedLinkedIds
1161 if (linkedId != null && wildcardLinkedIds == null
1162 && a.hasOption(Opt.STORED)
1163 && !storedLinkedIds.contains(linkedId))
1165 storedLinkedIds.add(linkedId);
1168 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1170 if (wildcardLinkedIds != null)
1172 for (String id : wildcardLinkedIds)
1174 // skip incorrectly stored wildcard ids!
1175 if (id == null || MATCHALLLINKEDIDS.equals(id)
1176 || MATCHOPENEDLINKEDIDS.equals(id))
1178 ArgValuesMap avm = linkedArgs.get(id);
1179 if (a.hasOption(Opt.REQUIREINPUT)
1180 && !avm.hasArgWithOption(Opt.INPUT))
1182 ArgValues tavs = avm.getOrCreateArgValues(a);
1183 tavs.setBoolean(b, argIndex);
1184 finaliseStoringArgValue(id, tavs);
1189 avs.setBoolean(b, argIndex);
1190 finaliseStoringArgValue(linkedId, avs);
1194 private void setNegated(String linkedId, ArgValues avs, boolean b)
1196 this.argValueOperation(Op.SETNEGATED, linkedId, avs, null, null, b, 0,
1200 private void NOTsetNegated(String linkedId, ArgValues avs, boolean b)
1203 if (linkedId != null && a.hasOption(Opt.STORED)
1204 && !storedLinkedIds.contains(linkedId))
1206 storedLinkedIds.add(linkedId);
1209 List<String> wildcardLinkedIds = null;
1210 if (a.hasOption(Opt.ALLOWALL))
1214 case MATCHALLLINKEDIDS:
1215 wildcardLinkedIds = getLinkedIds();
1217 case MATCHOPENEDLINKEDIDS:
1218 wildcardLinkedIds = this.storedLinkedIds;
1223 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1224 // to storedLinkedIds
1225 if (linkedId != null && wildcardLinkedIds == null
1226 && a.hasOption(Opt.STORED)
1227 && !storedLinkedIds.contains(linkedId))
1229 storedLinkedIds.add(linkedId);
1232 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1234 if (wildcardLinkedIds != null)
1236 for (String id : wildcardLinkedIds)
1238 // skip incorrectly stored wildcard ids!
1239 if (id == null || MATCHALLLINKEDIDS.equals(id)
1240 || MATCHOPENEDLINKEDIDS.equals(id))
1242 ArgValuesMap avm = linkedArgs.get(id);
1243 if (a.hasOption(Opt.REQUIREINPUT)
1244 && !avm.hasArgWithOption(Opt.INPUT))
1246 ArgValues tavs = avm.getOrCreateArgValues(a);
1256 private void incrementCount(String linkedId, ArgValues avs)
1258 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, avs, null, null,
1262 private void NOTincrementCount(String linkedId, ArgValues avs)
1266 List<String> wildcardLinkedIds = null;
1267 if (a.hasOption(Opt.ALLOWALL))
1271 case MATCHALLLINKEDIDS:
1272 wildcardLinkedIds = getLinkedIds();
1274 case MATCHOPENEDLINKEDIDS:
1275 wildcardLinkedIds = this.storedLinkedIds;
1280 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1281 // to storedLinkedIds
1282 if (linkedId != null && wildcardLinkedIds == null
1283 && a.hasOption(Opt.STORED)
1284 && !storedLinkedIds.contains(linkedId))
1286 storedLinkedIds.add(linkedId);
1289 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1291 if (wildcardLinkedIds != null)
1293 for (String id : wildcardLinkedIds)
1295 // skip incorrectly stored wildcard ids!
1296 if (id == null || MATCHALLLINKEDIDS.equals(id)
1297 || MATCHOPENEDLINKEDIDS.equals(id))
1299 ArgValuesMap avm = linkedArgs.get(id);
1300 if (a.hasOption(Opt.REQUIREINPUT)
1301 && !avm.hasArgWithOption(Opt.INPUT))
1303 ArgValues tavs = avm.getOrCreateArgValues(a);
1304 tavs.incrementCount();
1309 avs.incrementCount();
1315 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1318 // The following operations look for the "*" and "open*" linkedIds and add the
1319 // argvalue to all appropriate linkedId ArgValues if it does.
1320 // If subvals are supplied, they are inserted into all new set values.
1321 private void argValueOperation(Op op, String linkedId, ArgValues avs,
1322 SubVals sv, String v, boolean b, int argIndex, boolean doSubs)
1326 List<String> wildcardLinkedIds = null;
1327 if (a.hasOption(Opt.ALLOWALL))
1331 case MATCHALLLINKEDIDS:
1332 wildcardLinkedIds = getLinkedIds();
1334 case MATCHOPENEDLINKEDIDS:
1335 wildcardLinkedIds = this.storedLinkedIds;
1340 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1341 // to storedLinkedIds
1342 if (linkedId != null && wildcardLinkedIds == null
1343 && a.hasOption(Opt.STORED)
1344 && !storedLinkedIds.contains(linkedId))
1346 storedLinkedIds.add(linkedId);
1349 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1351 if (wildcardLinkedIds != null)
1353 for (String id : wildcardLinkedIds)
1355 // skip incorrectly stored wildcard ids!
1356 if (id == null || MATCHALLLINKEDIDS.equals(id)
1357 || MATCHOPENEDLINKEDIDS.equals(id))
1359 ArgValuesMap avm = linkedArgs.get(id);
1360 // don't set an output if there isn't an input
1361 if (a.hasOption(Opt.REQUIREINPUT)
1362 && !avm.hasArgWithOption(Opt.INPUT))
1365 ArgValues tavs = avm.getOrCreateArgValues(a);
1375 val = makeSubstitutions(v, id);
1376 sv = new SubVals(sv, val);
1378 tavs.addValue(sv, val, argIndex);
1384 val = makeSubstitutions(v, id);
1386 tavs.addValue(val, argIndex);
1388 finaliseStoringArgValue(id, tavs);
1392 tavs.setBoolean(b, argIndex);
1393 finaliseStoringArgValue(id, tavs);
1400 case INCREMENTCOUNT:
1401 tavs.incrementCount();
1411 else // no wildcard linkedId -- do it simpler
1421 val = makeSubstitutions(v, linkedId);
1422 sv = new SubVals(sv, val);
1424 avs.addValue(sv, val, argIndex);
1430 val = makeSubstitutions(v, linkedId);
1432 avs.addValue(val, argIndex);
1434 finaliseStoringArgValue(linkedId, avs);
1438 avs.setBoolean(b, argIndex);
1439 finaliseStoringArgValue(linkedId, avs);
1446 case INCREMENTCOUNT:
1447 avs.incrementCount();
1456 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1458 if (linkedArgs.containsKey(linkedId)
1459 && linkedArgs.get(linkedId) != null)
1460 return linkedArgs.get(linkedId);
1462 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1463 return linkedArgs.get(linkedId);