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 String typeName = argName.substring(dashPos + 1);
289 type = Type.valueOf(typeName);
290 } catch (IllegalArgumentException e)
294 argName = argName.substring(0, dashPos);
298 Arg a = argMap.get(argName);
299 // check for boolean prepended by "no" e.g. --nowrap
300 boolean negated = false;
301 if (a == null && argName.startsWith(NEGATESTRING) && argMap
302 .containsKey(argName.substring(NEGATESTRING.length())))
304 argName = argName.substring(NEGATESTRING.length());
305 a = argMap.get(argName);
309 // check for config errors
313 Console.error("Argument '" + arg + "' not recognised. Exiting.");
314 Jalview.exit("Invalid argument used." + System.lineSeparator()
315 + "Use" + System.lineSeparator() + "jalview "
316 + Arg.HELP.argString() + System.lineSeparator()
317 + "for a usage statement.", 13);
320 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
323 "Argument '" + a.argString() + "' is private. Ignoring.");
326 if (!a.hasOption(Opt.BOOLEAN) && negated)
328 // used "no" with a non-boolean option
329 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
330 + "' not a boolean option. Ignoring.");
333 if (!a.hasOption(Opt.STRING) && equalPos > -1)
335 // set --argname=value when arg does not accept values
336 Console.error("Argument '" + a.argString()
337 + "' does not expect a value (given as '" + arg
341 if (!a.hasOption(Opt.LINKED) && linkedId != null)
343 // set --argname[linkedId] when arg does not use linkedIds
344 Console.error("Argument '" + a.argString()
345 + "' does not expect a linked id (given as '" + arg
351 if (a.hasOption(Opt.STRING))
355 if (a.hasOption(Opt.GLOB))
357 // strip off and save the SubVals to be added individually later
358 globSubVals = new SubVals(val);
359 // make substitutions before looking for files
360 String fileGlob = makeSubstitutions(globSubVals.getContent(),
362 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
366 // val is already set -- will be saved in the ArgValue later in
372 // There is no "=" so value is next arg or args (possibly shell
374 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
376 // no value to take for arg, which wants a value
377 Console.error("Argument '" + a.getName()
378 + "' requires a value, none given. Ignoring.");
381 // deal with bash globs here (--arg val* is expanded before reaching
382 // the JVM). Note that SubVals cannot be used in this case.
383 // If using the --arg=val then the glob is preserved and Java globs
384 // will be used later. SubVals can be used.
385 if (a.hasOption(Opt.GLOB))
387 // if this is the first argument with a file list at the start of
388 // the args we add filenames from index i instead of i+1
389 globVals = getShellGlobbedFilenameValues(a, args,
390 openEachInitialFilenames ? i : i + 1);
394 val = args.get(i + 1);
399 // make NOACTION adjustments
400 // default and auto counter increments
403 linkedIdAutoCounter++;
405 else if (a == Arg.SUBSTITUTIONS)
407 substitutions = !negated;
409 else if (a == Arg.SETARGFILE)
413 else if (a == Arg.UNSETARGFILE)
417 else if (a == Arg.ALL)
419 allLinkedIds = !negated;
420 openedLinkedIds = false;
422 else if (a == Arg.OPENED)
424 openedLinkedIds = !negated;
425 allLinkedIds = false;
428 if (a.hasOption(Opt.STORED))
430 // reset the lastOpenedLinkedIds list
431 this.storedLinkedIds = new ArrayList<>();
434 // this is probably only Arg.NEW and Arg.OPEN
435 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
437 // use the next default prefixed OPENLINKEDID
438 defaultLinkedId(true);
441 String autoCounterString = null;
442 String defaultLinkedId = defaultLinkedId(false);
443 boolean usingDefaultLinkedId = false;
444 if (a.hasOption(Opt.LINKED))
446 if (linkedId == null)
448 if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
449 && val.startsWith(MATCHALLLINKEDIDS))
451 // --output=*.ext is shorthand for --all --output {basename}.ext
452 // (or --image=*.ext)
454 openedLinkedIds = false;
455 linkedId = MATCHALLLINKEDIDS;
456 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
457 + val.substring(MATCHALLLINKEDIDS.length());
459 else if (a.hasOption(Opt.OUTPUT) && a.hasOption(Opt.ALLOWALL)
460 && val.startsWith(MATCHOPENEDLINKEDIDS))
462 // --output=open*.ext is shorthand for --opened --output
464 // (or --image=open*.ext)
465 openedLinkedIds = true;
466 allLinkedIds = false;
467 linkedId = MATCHOPENEDLINKEDIDS;
468 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
469 + val.substring(MATCHOPENEDLINKEDIDS.length());
471 else if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
473 linkedId = MATCHALLLINKEDIDS;
475 else if (openedLinkedIds && a.hasOption(Opt.ALLOWALL))
477 linkedId = MATCHOPENEDLINKEDIDS;
481 // use default linkedId for linked arguments
482 linkedId = defaultLinkedId;
483 usingDefaultLinkedId = true;
484 Console.debug("Changing linkedId to '" + linkedId + "' from "
488 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
490 // turn {n} to the autoCounter
491 autoCounterString = Integer.toString(linkedIdAutoCounter);
492 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
495 "Changing linkedId to '" + linkedId + "' from " + arg);
497 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
499 // turn {++n} to the incremented autoCounter
500 autoCounterString = Integer.toString(++linkedIdAutoCounter);
501 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
504 "Changing linkedId to '" + linkedId + "' from " + arg);
508 // do not continue in this block for NOACTION args
509 if (a.hasOption(Opt.NOACTION))
512 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
514 // not dealing with both NODUPLICATEVALUES and GLOB
515 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
517 Console.error("Argument '" + a.argString()
518 + "' cannot contain a duplicate value ('" + val
519 + "'). Ignoring this and subsequent occurrences.");
523 // check for unique id
524 SubVals idsv = new SubVals(val);
525 String id = idsv.get(ArgValues.ID);
526 if (id != null && avm.hasId(a, id))
528 Console.error("Argument '" + a.argString()
529 + "' has a duplicate id ('" + id + "'). Ignoring.");
534 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
535 * DONE, need to check
537 ArgValues avs = avm.getOrCreateArgValues(a);
539 // store appropriate String value(s)
540 if (a.hasOption(Opt.STRING))
542 if (a.hasOption(Opt.GLOB) && globVals != null
543 && globVals.size() > 0)
545 Enumeration<String> gve = Collections.enumeration(globVals);
546 while (gve.hasMoreElements())
548 String v = gve.nextElement();
549 SubVals vsv = new SubVals(globSubVals, v);
550 addValue(linkedId, type, avs, vsv, v, argIndex++, true);
551 // if we're using defaultLinkedId and the arg increments the
553 if (gve.hasMoreElements() && usingDefaultLinkedId
554 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
556 // increment the default linkedId
557 linkedId = defaultLinkedId(true);
558 // get new avm and avs
559 avm = linkedArgs.get(linkedId);
560 avs = avm.getOrCreateArgValues(a);
566 addValue(linkedId, type, avs, val, argIndex, true);
569 else if (a.hasOption(Opt.BOOLEAN))
571 setBoolean(linkedId, type, avs, !negated, argIndex);
572 setNegated(linkedId, avs, negated);
574 else if (a.hasOption(Opt.UNARY))
576 setBoolean(linkedId, type, avs, true, argIndex);
579 // remove the '*' or 'open*' linkedId that should be empty if it was
581 if ((MATCHALLLINKEDIDS.equals(linkedId)
582 && linkedArgs.containsKey(linkedId))
583 || (MATCHOPENEDLINKEDIDS.equals(linkedId)
584 && linkedArgs.containsKey(linkedId)))
586 linkedArgs.remove(linkedId);
592 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
595 incrementCount(linkedId, avs);
598 // store in appropriate place
599 if (a.hasOption(Opt.LINKED))
601 // store the order of linkedIds
602 if (!linkedOrder.contains(linkedId))
603 linkedOrder.add(linkedId);
606 // store arg in the list of args used
607 if (!argList.contains(a))
611 private String defaultLinkedId(boolean increment)
613 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
614 .append(Integer.toString(defaultLinkedIdCounter)).toString();
617 while (linkedArgs.containsKey(defaultLinkedId))
619 defaultLinkedIdCounter++;
620 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
621 .append(Integer.toString(defaultLinkedIdCounter))
625 getOrCreateLinkedArgValuesMap(defaultLinkedId);
626 return defaultLinkedId;
629 public String makeSubstitutions(String val, String linkedId)
631 if (!this.substitutions || val == null)
636 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
638 int closeBracket = val.indexOf(']');
639 if (val.length() == closeBracket)
641 subvals = val.substring(0, closeBracket + 1);
642 rest = val.substring(closeBracket + 1);
649 if (rest.contains(LINKEDIDAUTOCOUNTER))
650 rest = rest.replace(LINKEDIDAUTOCOUNTER,
651 String.valueOf(linkedIdAutoCounter));
652 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
653 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
654 String.valueOf(++linkedIdAutoCounter));
655 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
656 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
657 String.valueOf(defaultLinkedIdCounter));
658 ArgValuesMap avm = linkedArgs.get(linkedId);
661 if (rest.contains(LINKEDIDBASENAME))
663 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
665 if (rest.contains(LINKEDIDEXTENSION))
667 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
669 if (rest.contains(LINKEDIDDIRNAME))
671 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
676 if (rest.contains(ARGFILEBASENAME))
678 rest = rest.replace(ARGFILEBASENAME,
679 FileUtils.getBasename(new File(argFile)));
681 if (rest.contains(ARGFILEDIRNAME))
683 rest = rest.replace(ARGFILEDIRNAME,
684 FileUtils.getDirname(new File(argFile)));
688 return new StringBuilder(subvals).append(rest).toString();
692 * A helper method to take a list of String args where we're expecting
693 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
694 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
695 * "file2", "file3"} *and remove these from the original list object* so that
696 * processing can continue from where it has left off, e.g. args has become
697 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
698 * carries on from the next --arg if available.
700 protected static List<String> getShellGlobbedFilenameValues(Arg a,
701 List<String> args, int i)
703 List<String> vals = new ArrayList<>();
704 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
706 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
707 if (!a.hasOption(Opt.GLOB))
713 public BootstrapArgs getBootstrapArgs()
715 return bootstrapArgs;
718 public boolean isSet(Arg a)
720 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
723 public boolean isSetAtAll(Arg a)
725 for (String linkedId : linkedOrder)
727 if (isSet(linkedId, a))
733 public boolean isSet(String linkedId, Arg a)
735 ArgValuesMap avm = linkedArgs.get(linkedId);
736 return avm == null ? false : avm.containsArg(a);
739 public boolean getBoolean(Arg a)
741 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
743 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
746 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
749 public boolean getBool(String linkedId, Arg a)
751 ArgValuesMap avm = linkedArgs.get(linkedId);
753 return a.getDefaultBoolValue();
754 ArgValues avs = avm.getArgValues(a);
755 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
758 public List<String> getLinkedIds()
763 public ArgValuesMap getLinkedArgs(String id)
765 return linkedArgs.get(id);
769 public String toString()
771 StringBuilder sb = new StringBuilder();
772 sb.append("UNLINKED\n");
773 sb.append(argValuesMapToString(linkedArgs.get(null)));
774 if (getLinkedIds() != null)
776 sb.append("LINKED\n");
777 for (String id : getLinkedIds())
779 // already listed these as UNLINKED args
783 ArgValuesMap avm = getLinkedArgs(id);
784 sb.append("ID: '").append(id).append("'\n");
785 sb.append(argValuesMapToString(avm));
788 return sb.toString();
791 private static String argValuesMapToString(ArgValuesMap avm)
795 StringBuilder sb = new StringBuilder();
796 for (Arg a : avm.getArgKeys())
798 ArgValues v = avm.getArgValues(a);
799 sb.append(v.toString());
802 return sb.toString();
805 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
806 boolean initsubstitutions, BootstrapArgs bsa)
808 List<File> argFiles = new ArrayList<>();
810 for (String pattern : argFilenameGlobs)
812 // I don't think we want to dedup files, making life easier
813 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
816 return parseArgFileList(argFiles, initsubstitutions, bsa);
819 public static ArgParser parseArgFileList(List<File> argFiles,
820 boolean initsubstitutions, BootstrapArgs bsa)
822 List<String> argsList = new ArrayList<>();
823 for (File argFile : argFiles)
825 if (!argFile.exists())
827 String message = Arg.ARGFILE.argString() + EQUALS + "\""
828 + argFile.getPath() + "\": File does not exist.";
829 Jalview.exit(message, 2);
833 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
834 .append(EQUALS).append(argFile.getCanonicalPath())
836 argsList.add(setargfile);
837 argsList.addAll(readArgFile(argFile));
838 argsList.add(Arg.UNSETARGFILE.argString());
839 } catch (IOException e)
841 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
842 + "\": File could not be read.";
843 Jalview.exit(message, 3);
846 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
848 return new ArgParser(argsList, initsubstitutions, true, bsa);
851 protected static List<String> readArgFile(File argFile)
853 List<String> args = new ArrayList<>();
854 if (argFile != null && argFile.exists())
858 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
860 if (line != null && line.length() > 0
861 && line.charAt(0) != ARGFILECOMMENT)
864 } catch (IOException e)
866 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
867 + "\": File could not be read.";
868 Console.debug(message, e);
869 Jalview.exit(message, 3);
875 public static enum Position
880 // get from following Arg of type a or subval of same name (lowercase)
881 public static String getValueFromSubValOrArg(ArgValuesMap avm,
882 ArgValue av, Arg a, SubVals sv)
884 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
887 // get from following Arg of type a or subval key or preference pref or
889 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
890 Arg a, SubVals sv, String key, String pref, String def)
892 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
896 // get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
897 // Arg of type a or subval key or preference pref or default def
898 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
899 Position pos, ArgValue av, SubVals sv, String key, String pref,
902 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
906 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
907 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
908 String key, String pref, String def)
913 if (sv != null && sv.has(key) && sv.get(key) != null)
915 value = ap == null ? sv.get(key)
916 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
918 else if (avm != null && avm.containsArg(a))
920 if (pos == Position.FIRST && avm.getValue(a) != null)
921 value = avm.getValue(a);
922 else if (pos == Position.BEFORE
923 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
924 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
925 else if (pos == Position.AFTER
926 && avm.getClosestNextArgValueOfArg(av, a) != null)
927 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
931 value = pref != null ? Cache.getDefault(pref, def) : def;
936 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
939 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
942 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
943 SubVals sv, String key, String pref, boolean def)
945 if ((key == null && a == null) || (sv == null && a == null))
948 boolean usingArgKey = false;
955 String nokey = ArgParser.NEGATESTRING + key;
957 // look for key or nokey in subvals first (if using Arg check options)
960 // check for true boolean
961 if (sv.has(key) && sv.get(key) != null)
965 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
968 "Looking for boolean in subval from non-boolean/non-unary Arg "
973 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
976 // check for negative boolean (subval "no..." will be "true")
977 if (sv.has(nokey) && sv.get(nokey) != null)
981 if (!(a.hasOption(Opt.BOOLEAN)))
984 "Looking for negative boolean in subval from non-boolean Arg "
989 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
994 if (avm != null && avm.containsArg(a))
995 return avm.getBoolean(a);
997 // return preference or default
998 return pref != null ? Cache.getDefault(pref, def) : def;
1001 // the following methods look for the "*" linkedId and add the argvalue to all
1002 // linkedId ArgValues if it does.
1003 // This version inserts the subvals sv into all created values
1004 private void addValue(String linkedId, Type type, ArgValues avs,
1005 SubVals sv, String v, int argIndex, boolean doSubs)
1007 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
1011 private void addValue(String linkedId, Type type, ArgValues avs, String v,
1012 int argIndex, boolean doSubs)
1014 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, null, v, false,
1018 private void setBoolean(String linkedId, Type type, ArgValues avs,
1019 boolean b, int argIndex)
1021 this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
1022 b, argIndex, false);
1025 private void setNegated(String linkedId, ArgValues avs, boolean b)
1027 this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
1031 private void incrementCount(String linkedId, ArgValues avs)
1033 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
1034 null, false, 0, false);
1039 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1042 // The following operations look for the "*" and "open*" linkedIds and add the
1043 // argvalue to all appropriate linkedId ArgValues if it does.
1044 // If subvals are supplied, they are inserted into all new set values.
1045 private void argValueOperation(Op op, String linkedId, Type type,
1046 ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
1051 List<String> wildcardLinkedIds = null;
1052 if (a.hasOption(Opt.ALLOWALL))
1056 case MATCHALLLINKEDIDS:
1057 wildcardLinkedIds = getLinkedIds();
1059 case MATCHOPENEDLINKEDIDS:
1060 wildcardLinkedIds = this.storedLinkedIds;
1065 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1066 // to storedLinkedIds
1067 if (linkedId != null && wildcardLinkedIds == null
1068 && a.hasOption(Opt.STORED)
1069 && !storedLinkedIds.contains(linkedId))
1071 storedLinkedIds.add(linkedId);
1074 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1076 if (wildcardLinkedIds != null)
1078 for (String id : wildcardLinkedIds)
1080 // skip incorrectly stored wildcard ids!
1081 if (id == null || MATCHALLLINKEDIDS.equals(id)
1082 || MATCHOPENEDLINKEDIDS.equals(id))
1084 ArgValuesMap avm = linkedArgs.get(id);
1085 // don't set an output if there isn't an input
1086 if (a.hasOption(Opt.REQUIREINPUT)
1087 && !avm.hasArgWithOption(Opt.INPUT))
1090 ArgValues tavs = avm.getOrCreateArgValues(a);
1100 val = makeSubstitutions(v, id);
1101 sv = new SubVals(sv, val);
1103 tavs.addValue(sv, type, val, argIndex, true);
1109 val = makeSubstitutions(v, id);
1111 tavs.addValue(type, val, argIndex, true);
1113 finaliseStoringArgValue(id, tavs);
1117 tavs.setBoolean(type, b, argIndex, true);
1118 finaliseStoringArgValue(id, tavs);
1122 tavs.setNegated(b, true);
1125 case INCREMENTCOUNT:
1126 tavs.incrementCount();
1136 else // no wildcard linkedId -- do it simpler
1146 val = makeSubstitutions(v, linkedId);
1147 sv = new SubVals(sv, val);
1149 avs.addValue(sv, type, val, argIndex, false);
1155 val = makeSubstitutions(v, linkedId);
1157 avs.addValue(type, val, argIndex, false);
1159 finaliseStoringArgValue(linkedId, avs);
1163 avs.setBoolean(type, b, argIndex, false);
1164 finaliseStoringArgValue(linkedId, avs);
1168 avs.setNegated(b, false);
1171 case INCREMENTCOUNT:
1172 avs.incrementCount();
1181 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1183 if (linkedArgs.containsKey(linkedId)
1184 && linkedArgs.get(linkedId) != null)
1185 return linkedArgs.get(linkedId);
1187 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1188 return linkedArgs.get(linkedId);