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 addValue(String linkedId, ArgValues avs, String v,
992 int argIndex, boolean doSubs)
994 this.argValueOperation(Op.ADDVALUE, linkedId, avs, null, v, false,
998 private void setBoolean(String linkedId, ArgValues avs, boolean b,
1001 this.argValueOperation(Op.SETBOOLEAN, linkedId, avs, null, null, b,
1005 private void setNegated(String linkedId, ArgValues avs, boolean b)
1007 this.argValueOperation(Op.SETNEGATED, linkedId, avs, null, null, b, 0,
1011 private void incrementCount(String linkedId, ArgValues avs)
1013 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, avs, null, null,
1019 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1022 // The following operations look for the "*" and "open*" linkedIds and add the
1023 // argvalue to all appropriate linkedId ArgValues if it does.
1024 // If subvals are supplied, they are inserted into all new set values.
1025 private void argValueOperation(Op op, String linkedId, ArgValues avs,
1026 SubVals sv, String v, boolean b, int argIndex, boolean doSubs)
1030 List<String> wildcardLinkedIds = null;
1031 if (a.hasOption(Opt.ALLOWALL))
1035 case MATCHALLLINKEDIDS:
1036 wildcardLinkedIds = getLinkedIds();
1038 case MATCHOPENEDLINKEDIDS:
1039 wildcardLinkedIds = this.storedLinkedIds;
1044 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1045 // to storedLinkedIds
1046 if (linkedId != null && wildcardLinkedIds == null
1047 && a.hasOption(Opt.STORED)
1048 && !storedLinkedIds.contains(linkedId))
1050 storedLinkedIds.add(linkedId);
1053 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1055 if (wildcardLinkedIds != null)
1057 for (String id : wildcardLinkedIds)
1059 // skip incorrectly stored wildcard ids!
1060 if (id == null || MATCHALLLINKEDIDS.equals(id)
1061 || MATCHOPENEDLINKEDIDS.equals(id))
1063 ArgValuesMap avm = linkedArgs.get(id);
1064 // don't set an output if there isn't an input
1065 if (a.hasOption(Opt.REQUIREINPUT)
1066 && !avm.hasArgWithOption(Opt.INPUT))
1069 ArgValues tavs = avm.getOrCreateArgValues(a);
1079 val = makeSubstitutions(v, id);
1080 sv = new SubVals(sv, val);
1082 tavs.addValue(sv, val, argIndex, true);
1088 val = makeSubstitutions(v, id);
1090 tavs.addValue(val, argIndex, true);
1092 finaliseStoringArgValue(id, tavs);
1096 tavs.setBoolean(b, argIndex, true);
1097 finaliseStoringArgValue(id, tavs);
1101 tavs.setNegated(b, true);
1104 case INCREMENTCOUNT:
1105 tavs.incrementCount();
1115 else // no wildcard linkedId -- do it simpler
1125 val = makeSubstitutions(v, linkedId);
1126 sv = new SubVals(sv, val);
1128 avs.addValue(sv, val, argIndex, false);
1134 val = makeSubstitutions(v, linkedId);
1136 avs.addValue(val, argIndex, false);
1138 finaliseStoringArgValue(linkedId, avs);
1142 avs.setBoolean(b, argIndex, false);
1143 finaliseStoringArgValue(linkedId, avs);
1147 avs.setNegated(b, false);
1150 case INCREMENTCOUNT:
1151 avs.incrementCount();
1160 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1162 if (linkedArgs.containsKey(linkedId)
1163 && linkedArgs.get(linkedId) != null)
1164 return linkedArgs.get(linkedId);
1166 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1167 return linkedArgs.get(linkedId);