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.bin.argparser.Arg.Type;
42 import jalview.util.FileUtils;
43 import jalview.util.HttpUtils;
45 public class ArgParser
47 protected static final String SINGLEDASH = "-";
49 protected static final String DOUBLEDASH = "--";
51 protected static final char EQUALS = '=';
53 protected static final String NEGATESTRING = "no";
55 // the default linked id prefix used for no id (not even square braces)
56 protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
58 // the linkedId string used to match all linkedIds seen so far
59 protected static final String MATCHALLLINKEDIDS = "*";
61 // the linkedId string used to match all of the last --open'ed linkedIds
62 protected static final String MATCHOPENEDLINKEDIDS = "open*";
64 // the counter added to the default linked id prefix
65 private int defaultLinkedIdCounter = 0;
67 // the substitution string used to use the defaultLinkedIdCounter
68 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
70 // the counter added to the default linked id prefix. NOW using
71 // linkedIdAutoCounter
72 // private int openLinkedIdCounter = 0;
74 // the linked id prefix used for --open files. NOW the same as DEFAULT
75 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
77 // the counter used for {n} substitutions
78 private int linkedIdAutoCounter = 0;
80 // the linked id substitution string used to increment the idCounter (and use
81 // the incremented value)
82 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
84 // the linked id substitution string used to use the idCounter
85 private static final String LINKEDIDAUTOCOUNTER = "{n}";
87 // the linked id substitution string used to use the filename extension of
90 private static final String LINKEDIDEXTENSION = "{extension}";
92 // the linked id substitution string used to use the base filename of --append
94 private static final String LINKEDIDBASENAME = "{basename}";
96 // the linked id substitution string used to use the dir path of --append
98 private static final String LINKEDIDDIRNAME = "{dirname}";
100 // the current argfile
101 private String argFile = null;
103 // the linked id substitution string used to use the dir path of the latest
105 private static final String ARGFILEBASENAME = "{argfilebasename}";
107 // the linked id substitution string used to use the dir path of the latest
109 private static final String ARGFILEDIRNAME = "{argfiledirname}";
111 // flag to say whether {n} subtitutions in output filenames should be made.
112 // Turn on and off with --substitutions and --nosubstitutions
114 private boolean substitutions = true;
116 // flag to say whether the default linkedId is the current default linked id
118 private boolean allLinkedIds = false;
120 // flag to say whether the default linkedId is the current default linked id
121 // or OPENED linkedIds
122 private boolean openedLinkedIds = false;
124 protected static final Map<String, Arg> argMap;
126 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
128 protected List<String> linkedOrder = new ArrayList<>();
130 protected List<String> storedLinkedIds = new ArrayList<>();
132 protected List<Arg> argList = new ArrayList<>();
134 private static final char ARGFILECOMMENT = '#';
136 private int argIndex = 0;
138 private BootstrapArgs bootstrapArgs = null;
142 argMap = new HashMap<>();
143 for (Arg a : EnumSet.allOf(Arg.class))
145 for (String argName : a.getNames())
147 if (argMap.containsKey(argName))
149 Console.warn("Trying to add argument name multiple times: '"
151 if (argMap.get(argName) != a)
154 "Trying to add argument name multiple times for different Args: '"
155 + argMap.get(argName).getName() + ":" + argName
156 + "' and '" + a.getName() + ":" + argName
161 argMap.put(argName, a);
166 public ArgParser(String[] args)
168 this(args, false, null);
171 public ArgParser(String[] args, boolean initsubstitutions,
174 // Make a mutable new ArrayList so that shell globbing parser works.
175 // (When shell file globbing is used, there are a sequence of non-Arg
176 // arguments (which are the expanded globbed filenames) that need to be
177 // consumed by the --append/--argfile/etc Arg which is most easily done by
178 // removing these filenames from the list one at a time. This can't be done
179 // with an ArrayList made with only Arrays.asList(String[] args). )
180 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
184 public ArgParser(List<String> args, boolean initsubstitutions)
186 this(args, initsubstitutions, false, null);
189 public ArgParser(List<String> args, boolean initsubstitutions,
190 boolean allowPrivate, BootstrapArgs bsa)
192 // do nothing if there are no "--" args and (some "-" args || >0 arg is
196 for (String arg : args)
198 if (arg.startsWith(DOUBLEDASH))
203 else if (arg.startsWith("-") || arg.equals("open"))
210 // leave it to the old style -- parse an empty list
211 parse(new ArrayList<String>(), false, false);
215 this.bootstrapArgs = bsa;
217 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
218 parse(args, initsubstitutions, allowPrivate);
221 private void parse(List<String> args, boolean initsubstitutions,
222 boolean allowPrivate)
224 this.substitutions = initsubstitutions;
225 boolean openEachInitialFilenames = true;
226 for (int i = 0; i < args.size(); i++)
228 String arg = args.get(i);
230 // If the first arguments do not start with "--" or "-" or is not "open"
231 // and` is a filename that exists it is probably a file/list of files to
232 // open so we fake an Arg.OPEN argument and when adding files only add the
233 // single arg[i] and increment the defaultLinkedIdCounter so that each of
234 // these files is opened separately.
235 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
236 && !arg.startsWith("-") && !arg.equals("open")
237 && (new File(arg).exists()
238 || HttpUtils.startsWithHttpOrHttps(arg)))
240 arg = Arg.OPEN.argString();
244 openEachInitialFilenames = false;
247 // look for double-dash, e.g. --arg
248 if (arg.startsWith(DOUBLEDASH))
250 String argName = null;
252 List<String> globVals = null; // for Opt.GLOB only
253 SubVals globSubVals = null; // also for use by Opt.GLOB only
254 String linkedId = null;
257 // look for equals e.g. --arg=value
258 int equalPos = arg.indexOf(EQUALS);
261 argName = arg.substring(DOUBLEDASH.length(), equalPos);
262 val = arg.substring(equalPos + 1);
266 argName = arg.substring(DOUBLEDASH.length());
269 // look for linked ID e.g. --arg[linkedID]
270 int idOpen = argName.indexOf('[');
271 int idClose = argName.indexOf(']');
272 if (idOpen > -1 && idClose == argName.length() - 1)
274 linkedId = argName.substring(idOpen + 1, idClose);
275 argName = argName.substring(0, idOpen);
278 // look for type modification e.g. --help-opening
279 int dashPos = argName.indexOf(SINGLEDASH);
282 String potentialArgName = argName.substring(0, dashPos);
283 Arg potentialArg = argMap.get(potentialArgName);
284 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
286 argName = argName.substring(0, dashPos);
287 String typeName = argName.substring(dashPos + 1);
288 type = Type.valueOf(typeName);
292 Arg a = argMap.get(argName);
293 // check for boolean prepended by "no" e.g. --nowrap
294 boolean negated = false;
295 if (a == null && argName.startsWith(NEGATESTRING) && argMap
296 .containsKey(argName.substring(NEGATESTRING.length())))
298 argName = argName.substring(NEGATESTRING.length());
299 a = argMap.get(argName);
303 // check for config errors
307 Console.error("Argument '" + arg + "' not recognised. Exiting.");
308 Jalview.exit("Invalid argument used." + System.lineSeparator()
309 + "Use" + System.lineSeparator() + "jalview "
310 + Arg.HELP.argString() + System.lineSeparator()
311 + "for a usage statement.", 13);
314 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
317 "Argument '" + a.argString() + "' is private. Ignoring.");
320 if (!a.hasOption(Opt.BOOLEAN) && negated)
322 // used "no" with a non-boolean option
323 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
324 + "' not a boolean option. Ignoring.");
327 if (!a.hasOption(Opt.STRING) && equalPos > -1)
329 // set --argname=value when arg does not accept values
330 Console.error("Argument '" + a.argString()
331 + "' does not expect a value (given as '" + arg
335 if (!a.hasOption(Opt.LINKED) && linkedId != null)
337 // set --argname[linkedId] when arg does not use linkedIds
338 Console.error("Argument '" + a.argString()
339 + "' does not expect a linked id (given as '" + arg
345 if (a.hasOption(Opt.STRING))
349 if (a.hasOption(Opt.GLOB))
351 // strip off and save the SubVals to be added individually later
352 globSubVals = new SubVals(val);
353 // make substitutions before looking for files
354 String fileGlob = makeSubstitutions(globSubVals.getContent(),
356 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
360 // val is already set -- will be saved in the ArgValue later in
366 // There is no "=" so value is next arg or args (possibly shell
368 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
370 // no value to take for arg, which wants a value
371 Console.error("Argument '" + a.getName()
372 + "' requires a value, none given. Ignoring.");
375 // deal with bash globs here (--arg val* is expanded before reaching
376 // the JVM). Note that SubVals cannot be used in this case.
377 // If using the --arg=val then the glob is preserved and Java globs
378 // will be used later. SubVals can be used.
379 if (a.hasOption(Opt.GLOB))
381 // if this is the first argument with a file list at the start of
382 // the args we add filenames from index i instead of i+1
383 globVals = getShellGlobbedFilenameValues(a, args,
384 openEachInitialFilenames ? i : i + 1);
388 val = args.get(i + 1);
393 // make NOACTION adjustments
394 // default and auto counter increments
397 linkedIdAutoCounter++;
399 else if (a == Arg.SUBSTITUTIONS)
401 substitutions = !negated;
403 else if (a == Arg.SETARGFILE)
407 else if (a == Arg.UNSETARGFILE)
411 else if (a == Arg.ALL)
413 allLinkedIds = !negated;
414 openedLinkedIds = false;
416 else if (a == Arg.OPENED)
418 openedLinkedIds = !negated;
419 allLinkedIds = false;
422 if (a.hasOption(Opt.STORED))
424 // reset the lastOpenedLinkedIds list
425 this.storedLinkedIds = new ArrayList<>();
428 // this is probably only Arg.NEW and Arg.OPEN
429 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
431 // use the next default prefixed OPENLINKEDID
432 defaultLinkedId(true);
435 String autoCounterString = null;
436 String defaultLinkedId = defaultLinkedId(false);
437 boolean usingDefaultLinkedId = false;
438 if (a.hasOption(Opt.LINKED))
440 if (linkedId == null)
442 if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
443 && val.startsWith(MATCHALLLINKEDIDS))
445 // --output=*.ext is shorthand for --all --output {basename}.ext
446 // (or --image=*.ext)
448 openedLinkedIds = false;
449 linkedId = MATCHALLLINKEDIDS;
450 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
451 + val.substring(MATCHALLLINKEDIDS.length());
453 else if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
454 && val.startsWith(MATCHOPENEDLINKEDIDS))
456 // --output=open*.ext is shorthand for --opened --output
458 // (or --image=open*.ext)
459 openedLinkedIds = true;
460 allLinkedIds = false;
461 linkedId = MATCHOPENEDLINKEDIDS;
462 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
463 + val.substring(MATCHOPENEDLINKEDIDS.length());
465 else if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
467 linkedId = MATCHALLLINKEDIDS;
469 else if (openedLinkedIds && a.hasOption(Opt.ALLOWALL))
471 linkedId = MATCHOPENEDLINKEDIDS;
475 // use default linkedId for linked arguments
476 linkedId = defaultLinkedId;
477 usingDefaultLinkedId = true;
478 Console.debug("Changing linkedId to '" + linkedId + "' from "
482 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
484 // turn {n} to the autoCounter
485 autoCounterString = Integer.toString(linkedIdAutoCounter);
486 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
489 "Changing linkedId to '" + linkedId + "' from " + arg);
491 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
493 // turn {++n} to the incremented autoCounter
494 autoCounterString = Integer.toString(++linkedIdAutoCounter);
495 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
498 "Changing linkedId to '" + linkedId + "' from " + arg);
502 // do not continue in this block for NOACTION args
503 if (a.hasOption(Opt.NOACTION))
506 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
508 // not dealing with both NODUPLICATEVALUES and GLOB
509 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
511 Console.error("Argument '" + a.argString()
512 + "' cannot contain a duplicate value ('" + val
513 + "'). Ignoring this and subsequent occurrences.");
517 // check for unique id
518 SubVals idsv = new SubVals(val);
519 String id = idsv.get(ArgValues.ID);
520 if (id != null && avm.hasId(a, id))
522 Console.error("Argument '" + a.argString()
523 + "' has a duplicate id ('" + id + "'). Ignoring.");
528 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
529 * DONE, need to check
531 ArgValues avs = avm.getOrCreateArgValues(a);
533 // store appropriate String value(s)
534 if (a.hasOption(Opt.STRING))
536 if (a.hasOption(Opt.GLOB) && globVals != null
537 && globVals.size() > 0)
539 Enumeration<String> gve = Collections.enumeration(globVals);
540 while (gve.hasMoreElements())
542 String v = gve.nextElement();
543 SubVals vsv = new SubVals(globSubVals, v);
544 addValue(linkedId, type, avs, vsv, v, argIndex++, true);
545 // if we're using defaultLinkedId and the arg increments the
547 if (gve.hasMoreElements() && usingDefaultLinkedId
548 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
550 // increment the default linkedId
551 linkedId = defaultLinkedId(true);
552 // get new avm and avs
553 avm = linkedArgs.get(linkedId);
554 avs = avm.getOrCreateArgValues(a);
560 addValue(linkedId, type, avs, val, argIndex, true);
563 else if (a.hasOption(Opt.BOOLEAN))
565 setBoolean(linkedId, type, avs, !negated, argIndex);
566 setNegated(linkedId, avs, negated);
568 else if (a.hasOption(Opt.UNARY))
570 setBoolean(linkedId, type, avs, true, argIndex);
573 // remove the '*' or 'open*' linkedId that should be empty if it was
575 if ((MATCHALLLINKEDIDS.equals(linkedId)
576 && linkedArgs.containsKey(linkedId))
577 || (MATCHOPENEDLINKEDIDS.equals(linkedId)
578 && linkedArgs.containsKey(linkedId)))
580 linkedArgs.remove(linkedId);
586 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
589 incrementCount(linkedId, avs);
592 // store in appropriate place
593 if (a.hasOption(Opt.LINKED))
595 // store the order of linkedIds
596 if (!linkedOrder.contains(linkedId))
597 linkedOrder.add(linkedId);
600 // store arg in the list of args used
601 if (!argList.contains(a))
605 private String defaultLinkedId(boolean increment)
607 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
608 .append(Integer.toString(defaultLinkedIdCounter)).toString();
611 while (linkedArgs.containsKey(defaultLinkedId))
613 defaultLinkedIdCounter++;
614 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
615 .append(Integer.toString(defaultLinkedIdCounter))
619 getOrCreateLinkedArgValuesMap(defaultLinkedId);
620 return defaultLinkedId;
623 public String makeSubstitutions(String val, String linkedId)
625 if (!this.substitutions || val == null)
630 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
632 int closeBracket = val.indexOf(']');
633 if (val.length() == closeBracket)
635 subvals = val.substring(0, closeBracket + 1);
636 rest = val.substring(closeBracket + 1);
643 if (rest.contains(LINKEDIDAUTOCOUNTER))
644 rest = rest.replace(LINKEDIDAUTOCOUNTER,
645 String.valueOf(linkedIdAutoCounter));
646 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
647 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
648 String.valueOf(++linkedIdAutoCounter));
649 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
650 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
651 String.valueOf(defaultLinkedIdCounter));
652 ArgValuesMap avm = linkedArgs.get(linkedId);
655 if (rest.contains(LINKEDIDBASENAME))
657 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
659 if (rest.contains(LINKEDIDEXTENSION))
661 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
663 if (rest.contains(LINKEDIDDIRNAME))
665 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
670 if (rest.contains(ARGFILEBASENAME))
672 rest = rest.replace(ARGFILEBASENAME,
673 FileUtils.getBasename(new File(argFile)));
675 if (rest.contains(ARGFILEDIRNAME))
677 rest = rest.replace(ARGFILEDIRNAME,
678 FileUtils.getDirname(new File(argFile)));
682 return new StringBuilder(subvals).append(rest).toString();
686 * A helper method to take a list of String args where we're expecting
687 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
688 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
689 * "file2", "file3"} *and remove these from the original list object* so that
690 * processing can continue from where it has left off, e.g. args has become
691 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
692 * carries on from the next --arg if available.
694 protected static List<String> getShellGlobbedFilenameValues(Arg a,
695 List<String> args, int i)
697 List<String> vals = new ArrayList<>();
698 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
700 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
701 if (!a.hasOption(Opt.GLOB))
707 public BootstrapArgs getBootstrapArgs()
709 return bootstrapArgs;
712 public boolean isSet(Arg a)
714 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
717 public boolean isSetAtAll(Arg a)
719 for (String linkedId : linkedOrder)
721 if (isSet(linkedId, a))
727 public boolean isSet(String linkedId, Arg a)
729 ArgValuesMap avm = linkedArgs.get(linkedId);
730 return avm == null ? false : avm.containsArg(a);
733 public boolean getBoolean(Arg a)
735 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
737 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
740 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
743 public boolean getBool(String linkedId, Arg a)
745 ArgValuesMap avm = linkedArgs.get(linkedId);
747 return a.getDefaultBoolValue();
748 ArgValues avs = avm.getArgValues(a);
749 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
752 public List<String> getLinkedIds()
757 public ArgValuesMap getLinkedArgs(String id)
759 return linkedArgs.get(id);
763 public String toString()
765 StringBuilder sb = new StringBuilder();
766 sb.append("UNLINKED\n");
767 sb.append(argValuesMapToString(linkedArgs.get(null)));
768 if (getLinkedIds() != null)
770 sb.append("LINKED\n");
771 for (String id : getLinkedIds())
773 // already listed these as UNLINKED args
777 ArgValuesMap avm = getLinkedArgs(id);
778 sb.append("ID: '").append(id).append("'\n");
779 sb.append(argValuesMapToString(avm));
782 return sb.toString();
785 private static String argValuesMapToString(ArgValuesMap avm)
789 StringBuilder sb = new StringBuilder();
790 for (Arg a : avm.getArgKeys())
792 ArgValues v = avm.getArgValues(a);
793 sb.append(v.toString());
796 return sb.toString();
799 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
800 boolean initsubstitutions, BootstrapArgs bsa)
802 List<File> argFiles = new ArrayList<>();
804 for (String pattern : argFilenameGlobs)
806 // I don't think we want to dedup files, making life easier
807 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
810 return parseArgFileList(argFiles, initsubstitutions, bsa);
813 public static ArgParser parseArgFileList(List<File> argFiles,
814 boolean initsubstitutions, BootstrapArgs bsa)
816 List<String> argsList = new ArrayList<>();
817 for (File argFile : argFiles)
819 if (!argFile.exists())
821 String message = Arg.ARGFILE.argString() + EQUALS + "\""
822 + argFile.getPath() + "\": File does not exist.";
823 Jalview.exit(message, 2);
827 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
828 .append(EQUALS).append(argFile.getCanonicalPath())
830 argsList.add(setargfile);
831 argsList.addAll(readArgFile(argFile));
832 argsList.add(Arg.UNSETARGFILE.argString());
833 } catch (IOException e)
835 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
836 + "\": File could not be read.";
837 Jalview.exit(message, 3);
840 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
842 return new ArgParser(argsList, initsubstitutions, true, bsa);
845 protected static List<String> readArgFile(File argFile)
847 List<String> args = new ArrayList<>();
848 if (argFile != null && argFile.exists())
852 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
854 if (line != null && line.length() > 0
855 && line.charAt(0) != ARGFILECOMMENT)
858 } catch (IOException e)
860 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
861 + "\": File could not be read.";
862 Console.debug(message, e);
863 Jalview.exit(message, 3);
869 public static enum Position
874 // get from following Arg of type a or subval of same name (lowercase)
875 public static String getValueFromSubValOrArg(ArgValuesMap avm,
876 ArgValue av, Arg a, SubVals sv)
878 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
881 // get from following Arg of type a or subval key or preference pref or
883 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
884 Arg a, SubVals sv, String key, String pref, String def)
886 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
890 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
891 // Arg of type a or subval key or preference pref or default def
892 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
893 Position pos, ArgValue av, SubVals sv, String key, String pref,
896 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
900 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
901 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
902 String key, String pref, String def)
907 if (sv != null && sv.has(key) && sv.get(key) != null)
909 value = ap == null ? sv.get(key)
910 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
912 else if (avm != null && avm.containsArg(a))
914 if (pos == Position.FIRST && avm.getValue(a) != null)
915 value = avm.getValue(a);
916 else if (pos == Position.BEFORE
917 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
918 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
919 else if (pos == Position.AFTER
920 && avm.getClosestNextArgValueOfArg(av, a) != null)
921 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
925 value = pref != null ? Cache.getDefault(pref, def) : def;
930 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
933 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
936 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
937 SubVals sv, String key, String pref, boolean def)
939 if ((key == null && a == null) || (sv == null && a == null))
942 boolean usingArgKey = false;
949 String nokey = ArgParser.NEGATESTRING + key;
951 // look for key or nokey in subvals first (if using Arg check options)
954 // check for true boolean
955 if (sv.has(key) && sv.get(key) != null)
959 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
962 "Looking for boolean in subval from non-boolean/non-unary Arg "
967 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
970 // check for negative boolean (subval "no..." will be "true")
971 if (sv.has(nokey) && sv.get(nokey) != null)
975 if (!(a.hasOption(Opt.BOOLEAN)))
978 "Looking for negative boolean in subval from non-boolean Arg "
983 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
988 if (avm != null && avm.containsArg(a))
989 return avm.getBoolean(a);
991 // return preference or default
992 return pref != null ? Cache.getDefault(pref, def) : def;
995 // the following methods look for the "*" linkedId and add the argvalue to all
996 // linkedId ArgValues if it does.
997 // This version inserts the subvals sv into all created values
998 private void addValue(String linkedId, Type type, ArgValues avs,
999 SubVals sv, String v, int argIndex, boolean doSubs)
1001 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
1005 private void addValue(String linkedId, Type type, ArgValues avs, String v,
1006 int argIndex, boolean doSubs)
1008 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, null, v, false,
1012 private void setBoolean(String linkedId, Type type, ArgValues avs,
1013 boolean b, int argIndex)
1015 this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
1016 b, argIndex, false);
1019 private void setNegated(String linkedId, ArgValues avs, boolean b)
1021 this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
1025 private void incrementCount(String linkedId, ArgValues avs)
1027 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
1028 null, false, 0, false);
1033 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1036 // The following operations look for the "*" and "open*" linkedIds and add the
1037 // argvalue to all appropriate linkedId ArgValues if it does.
1038 // If subvals are supplied, they are inserted into all new set values.
1039 private void argValueOperation(Op op, String linkedId, Type type,
1040 ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
1045 List<String> wildcardLinkedIds = null;
1046 if (a.hasOption(Opt.ALLOWALL))
1050 case MATCHALLLINKEDIDS:
1051 wildcardLinkedIds = getLinkedIds();
1053 case MATCHOPENEDLINKEDIDS:
1054 wildcardLinkedIds = this.storedLinkedIds;
1059 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1060 // to storedLinkedIds
1061 if (linkedId != null && wildcardLinkedIds == null
1062 && a.hasOption(Opt.STORED)
1063 && !storedLinkedIds.contains(linkedId))
1065 storedLinkedIds.add(linkedId);
1068 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1070 if (wildcardLinkedIds != null)
1072 for (String id : wildcardLinkedIds)
1074 // skip incorrectly stored wildcard ids!
1075 if (id == null || MATCHALLLINKEDIDS.equals(id)
1076 || MATCHOPENEDLINKEDIDS.equals(id))
1078 ArgValuesMap avm = linkedArgs.get(id);
1079 // don't set an output if there isn't an input
1080 if (a.hasOption(Opt.REQUIREINPUT)
1081 && !avm.hasArgWithOption(Opt.INPUT))
1084 ArgValues tavs = avm.getOrCreateArgValues(a);
1094 val = makeSubstitutions(v, id);
1095 sv = new SubVals(sv, val);
1097 tavs.addValue(sv, type, val, argIndex, true);
1103 val = makeSubstitutions(v, id);
1105 tavs.addValue(type, val, argIndex, true);
1107 finaliseStoringArgValue(id, tavs);
1111 tavs.setBoolean(type, b, argIndex, true);
1112 finaliseStoringArgValue(id, tavs);
1116 tavs.setNegated(b, true);
1119 case INCREMENTCOUNT:
1120 tavs.incrementCount();
1130 else // no wildcard linkedId -- do it simpler
1140 val = makeSubstitutions(v, linkedId);
1141 sv = new SubVals(sv, val);
1143 avs.addValue(sv, type, val, argIndex, false);
1149 val = makeSubstitutions(v, linkedId);
1151 avs.addValue(type, val, argIndex, false);
1153 finaliseStoringArgValue(linkedId, avs);
1157 avs.setBoolean(type, b, argIndex, false);
1158 finaliseStoringArgValue(linkedId, avs);
1162 avs.setNegated(b, false);
1165 case INCREMENTCOUNT:
1166 avs.incrementCount();
1175 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1177 if (linkedArgs.containsKey(linkedId)
1178 && linkedArgs.get(linkedId) != null)
1179 return linkedArgs.get(linkedId);
1181 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1182 return linkedArgs.get(linkedId);