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 public static final char EQUALS = '=';
53 public static final String STDOUTFILENAME = "-";
55 protected static final String NEGATESTRING = "no";
58 * the default linked id prefix used for no id (ie when not even square braces
61 protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
64 * the linkedId string used to match all linkedIds seen so far
66 protected static final String MATCHALLLINKEDIDS = "*";
69 * the linkedId string used to match all of the last --open'ed linkedIds
71 protected static final String MATCHOPENEDLINKEDIDS = "open*";
74 * the counter added to the default linked id prefix
76 private int defaultLinkedIdCounter = 0;
79 * the substitution string used to use the defaultLinkedIdCounter
81 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
84 * the linked id prefix used for --open files. NOW the same as DEFAULT
86 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
89 * the counter used for {n} substitutions
91 private int linkedIdAutoCounter = 0;
94 * the linked id substitution string used to increment the idCounter (and use
95 * the incremented value)
97 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
100 * the linked id substitution string used to use the idCounter
102 private static final String LINKEDIDAUTOCOUNTER = "{n}";
105 * the linked id substitution string used to use the filename extension of
108 private static final String LINKEDIDEXTENSION = "{extension}";
111 * the linked id substitution string used to use the base filename of --append
114 private static final String LINKEDIDBASENAME = "{basename}";
117 * the linked id substitution string used to use the dir path of --append or
120 private static final String LINKEDIDDIRNAME = "{dirname}";
123 * the current argfile
125 private String argFile = null;
128 * the linked id substitution string used to use the dir path of the latest
130 /** --argfile name */
131 private static final String ARGFILEBASENAME = "{argfilebasename}";
134 * the linked id substitution string used to use the dir path of the latest
137 private static final String ARGFILEDIRNAME = "{argfiledirname}";
140 * flag to say whether {n} subtitutions in output filenames should be made.
141 * Turn on and off with --substitutions and --nosubstitutions Start with it on
143 private boolean substitutions = true;
146 * flag to say whether the default linkedId is the current default linked id
150 private boolean allLinkedIds = false;
153 * flag to say whether the structure arguments should be applied to all
154 * structures with this linked id
156 private boolean allStructures = false;
158 protected static final Map<String, Arg> argMap;
160 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
162 protected List<String> linkedOrder = new ArrayList<>();
164 protected List<String> storedLinkedIds = new ArrayList<>();
166 protected List<Arg> argList = new ArrayList<>();
168 private static final char ARGFILECOMMENT = '#';
170 private int argIndex = 0;
172 private BootstrapArgs bootstrapArgs = null;
176 argMap = new HashMap<>();
177 for (Arg a : EnumSet.allOf(Arg.class))
179 for (String argName : a.getNames())
181 if (argMap.containsKey(argName))
183 Console.warn("Trying to add argument name multiple times: '"
185 if (argMap.get(argName) != a)
188 "Trying to add argument name multiple times for different Args: '"
189 + argMap.get(argName).getName() + ":" + argName
190 + "' and '" + a.getName() + ":" + argName
195 argMap.put(argName, a);
200 public ArgParser(String[] args)
202 this(args, false, null);
205 public ArgParser(String[] args, boolean initsubstitutions,
209 * Make a mutable new ArrayList so that shell globbing parser works.
210 * (When shell file globbing is used, there are a sequence of non-Arg
211 * arguments (which are the expanded globbed filenames) that need to be
212 * consumed by the --append/--argfile/etc Arg which is most easily done
213 * by removing these filenames from the list one at a time. This can't be
214 * done with an ArrayList made with only Arrays.asList(String[] args) as
215 * that is not mutable. )
217 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
221 public ArgParser(List<String> args, boolean initsubstitutions)
223 this(args, initsubstitutions, false, null);
226 public ArgParser(List<String> args, boolean initsubstitutions,
227 boolean allowPrivate, BootstrapArgs bsa)
229 // do nothing if there are no "--" args and (some "-" args || >0 arg is
233 for (String arg : args)
235 if (arg.startsWith(DOUBLEDASH))
240 else if (arg.startsWith("-") || arg.equals("open"))
247 // leave it to the old style -- parse an empty list
248 parse(new ArrayList<String>(), false, false);
252 this.bootstrapArgs = bsa;
254 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
255 parse(args, initsubstitutions, allowPrivate);
258 private void parse(List<String> args, boolean initsubstitutions,
259 boolean allowPrivate)
261 this.substitutions = initsubstitutions;
264 * If the first argument does not start with "--" or "-" or is not "open",
265 * and is a filename that exists or a URL, it is probably a file/list of
266 * files to open so we insert an Arg.OPEN argument before it. This will
267 * mean the list of files at the start of the arguments are all opened
272 String arg0 = args.get(0);
274 && (!arg0.startsWith(DOUBLEDASH) && !arg0.startsWith("-")
275 && !arg0.equals("open") && (new File(arg0).exists()
276 || HttpUtils.startsWithHttpOrHttps(arg0))))
278 // insert "--open" at the start
279 args.add(0, Arg.OPEN.argString());
283 for (int i = 0; i < args.size(); i++)
285 String arg = args.get(i);
287 // look for double-dash, e.g. --arg
288 if (arg.startsWith(DOUBLEDASH))
290 String argName = null;
292 List<String> globVals = null; // for Opt.GLOB only
293 SubVals globSubVals = null; // also for use by Opt.GLOB only
294 String linkedId = null;
297 // look for equals e.g. --arg=value
298 int equalPos = arg.indexOf(EQUALS);
301 argName = arg.substring(DOUBLEDASH.length(), equalPos);
302 val = arg.substring(equalPos + 1);
306 argName = arg.substring(DOUBLEDASH.length());
309 // look for linked ID e.g. --arg[linkedID]
310 int idOpen = argName.indexOf('[');
311 int idClose = argName.indexOf(']');
312 if (idOpen > -1 && idClose == argName.length() - 1)
314 linkedId = argName.substring(idOpen + 1, idClose);
315 argName = argName.substring(0, idOpen);
318 // look for type modification e.g. --help-opening
319 int dashPos = argName.indexOf(SINGLEDASH);
322 String potentialArgName = argName.substring(0, dashPos);
323 Arg potentialArg = argMap.get(potentialArgName);
324 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
326 String typeName = argName.substring(dashPos + 1);
329 type = Type.valueOf(typeName);
330 } catch (IllegalArgumentException e)
334 argName = argName.substring(0, dashPos);
338 Arg a = argMap.get(argName);
339 // check for boolean prepended by "no" e.g. --nowrap
340 boolean negated = false;
343 if (argName.startsWith(NEGATESTRING) && argMap
344 .containsKey(argName.substring(NEGATESTRING.length())))
346 argName = argName.substring(NEGATESTRING.length());
347 a = argMap.get(argName);
352 // after all other args, look for Opt.PREFIXKEV args if still not
354 for (Arg potentialArg : EnumSet.allOf(Arg.class))
356 if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null
357 && argName.startsWith(potentialArg.getName())
360 val = argName.substring(potentialArg.getName().length())
362 argName = argName.substring(0,
363 potentialArg.getName().length());
371 // check for config errors
375 Console.error("Argument '" + arg + "' not recognised. Exiting.");
376 Jalview.exit("Invalid argument used." + System.lineSeparator()
377 + "Use" + System.lineSeparator() + "jalview "
378 + Arg.HELP.argString() + System.lineSeparator()
379 + "for a usage statement.", 13);
382 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
385 "Argument '" + a.argString() + "' is private. Ignoring.");
388 if (!a.hasOption(Opt.BOOLEAN) && negated)
390 // used "no" with a non-boolean option
391 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
392 + "' not a boolean option. Ignoring.");
395 if (!a.hasOption(Opt.STRING) && equalPos > -1)
397 // set --argname=value when arg does not accept values
398 Console.error("Argument '" + a.argString()
399 + "' does not expect a value (given as '" + arg
403 if (!a.hasOption(Opt.LINKED) && linkedId != null)
405 // set --argname[linkedId] when arg does not use linkedIds
406 Console.error("Argument '" + a.argString()
407 + "' does not expect a linked id (given as '" + arg
413 if (a.hasOption(Opt.STRING))
417 if (a.hasOption(Opt.GLOB))
419 // strip off and save the SubVals to be added individually later
420 globSubVals = new SubVals(val);
421 // make substitutions before looking for files
422 String fileGlob = makeSubstitutions(globSubVals.getContent(),
424 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
428 // val is already set -- will be saved in the ArgValue later in
434 // There is no "=" so value is next arg or args (possibly shell
436 if (i + 1 >= args.size())
438 // no value to take for arg, which wants a value
439 Console.error("Argument '" + a.getName()
440 + "' requires a value, none given. Ignoring.");
443 // deal with bash globs here (--arg val* is expanded before reaching
444 // the JVM). Note that SubVals cannot be used in this case.
445 // If using the --arg=val then the glob is preserved and Java globs
446 // will be used later. SubVals can be used.
447 if (a.hasOption(Opt.GLOB))
449 // if this is the first argument with a file list at the start of
450 // the args we add filenames from index i instead of i+1
451 globVals = getShellGlobbedFilenameValues(a, args, i + 1);
455 val = args.get(i + 1);
460 // make NOACTION adjustments
461 // default and auto counter increments
464 linkedIdAutoCounter++;
466 else if (a == Arg.SUBSTITUTIONS)
468 substitutions = !negated;
470 else if (a == Arg.SETARGFILE)
474 else if (a == Arg.UNSETARGFILE)
478 else if (a == Arg.ALL)
480 allLinkedIds = !negated;
482 else if (a == Arg.ALLSTRUCTURES)
484 allStructures = !negated;
487 if (a.hasOption(Opt.STORED))
489 // reset the lastOpenedLinkedIds list
490 this.storedLinkedIds = new ArrayList<>();
493 // this is probably only Arg.NEW and Arg.OPEN
494 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
496 // use the next default prefixed OPENLINKEDID
497 defaultLinkedId(true);
500 String autoCounterString = null;
501 String defaultLinkedId = defaultLinkedId(false);
502 boolean usingDefaultLinkedId = false;
503 if (a.hasOption(Opt.LINKED))
505 if (linkedId == null)
507 if (a.hasOption(Opt.OUTPUTFILE) && a.hasOption(Opt.ALLOWMULTIID)
508 && val.startsWith(MATCHALLLINKEDIDS))
510 // --output=*.ext is shorthand for --all --output {basename}.ext
511 // (or --image=*.ext)
513 linkedId = MATCHALLLINKEDIDS;
514 val = LINKEDIDDIRNAME + File.separator + LINKEDIDBASENAME
515 + val.substring(MATCHALLLINKEDIDS.length());
517 else if (allLinkedIds && a.hasOption(Opt.ALLOWMULTIID))
519 linkedId = MATCHALLLINKEDIDS;
521 else if (a.hasOption(Opt.ALLOWMULTIID)
522 && this.storedLinkedIds != null
523 && this.storedLinkedIds.size() > 0)
525 linkedId = MATCHOPENEDLINKEDIDS;
529 // use default linkedId for linked arguments
530 linkedId = defaultLinkedId;
531 usingDefaultLinkedId = true;
532 Console.debug("Changing linkedId to '" + linkedId + "' from "
538 if (linkedId.contains(LINKEDIDAUTOCOUNTER))
540 // turn {n} to the autoCounter
541 autoCounterString = Integer.toString(linkedIdAutoCounter);
542 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
544 Console.debug("Changing linkedId to '" + linkedId + "' from "
547 if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
549 // turn {++n} to the incremented autoCounter
550 autoCounterString = Integer.toString(++linkedIdAutoCounter);
551 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
553 Console.debug("Changing linkedId to '" + linkedId + "' from "
559 // do not continue in this block for NOACTION args
560 if (a.hasOption(Opt.NOACTION))
563 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
565 // not dealing with both NODUPLICATEVALUES and GLOB
566 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
568 Console.error("Argument '" + a.argString()
569 + "' cannot contain a duplicate value ('" + val
570 + "'). Ignoring this and subsequent occurrences.");
574 // check for unique id
575 SubVals subvals = new SubVals(val);
576 boolean addNewSubVals = false;
577 String id = subvals.get(ArgValues.ID);
578 if (id != null && avm.hasId(a, id))
580 Console.error("Argument '" + a.argString()
581 + "' has a duplicate id ('" + id + "'). Ignoring.");
585 // set allstructures to all non-primary structure options in this linked
586 // id if --allstructures has been set
588 && (a.getType() == Type.STRUCTURE
589 || a.getType() == Type.STRUCTUREIMAGE)
590 && !a.hasOption(Opt.PRIMARY))
592 if (!subvals.has(Arg.ALLSTRUCTURES.getName()))
593 // && !subvals.has("structureid"))
595 subvals.put(Arg.ALLSTRUCTURES.getName(), "true");
596 addNewSubVals = true;
600 ArgValues avs = avm.getOrCreateArgValues(a);
602 // store appropriate String value(s)
603 if (a.hasOption(Opt.STRING))
605 if (a.hasOption(Opt.GLOB) && globVals != null
606 && globVals.size() > 0)
608 Enumeration<String> gve = Collections.enumeration(globVals);
609 while (gve.hasMoreElements())
611 String v = gve.nextElement();
612 SubVals vsv = new SubVals(globSubVals, v);
613 addValue(linkedId, type, avs, vsv, v, argIndex++, true);
614 // if we're using defaultLinkedId and the arg increments the
616 if (gve.hasMoreElements() && usingDefaultLinkedId
617 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
619 // increment the default linkedId
620 linkedId = defaultLinkedId(true);
621 // get new avm and avs
622 avm = linkedArgs.get(linkedId);
623 avs = avm.getOrCreateArgValues(a);
629 // addValue(linkedId, type, avs, val, argIndex, true);
630 addValue(linkedId, type, avs, addNewSubVals ? subvals : null,
631 val, argIndex, true);
634 else if (a.hasOption(Opt.BOOLEAN))
636 setBoolean(linkedId, type, avs, !negated, argIndex);
637 setNegated(linkedId, avs, negated);
639 else if (a.hasOption(Opt.UNARY))
641 setBoolean(linkedId, type, avs, true, argIndex);
644 // remove the '*' or 'open*' linkedId that should be empty if it was
646 if ((MATCHALLLINKEDIDS.equals(linkedId)
647 || MATCHOPENEDLINKEDIDS.equals(linkedId))
648 && linkedArgs.containsKey(linkedId))
650 linkedArgs.remove(linkedId);
656 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
659 incrementCount(linkedId, avs);
662 // store in appropriate place
663 if (a.hasOption(Opt.LINKED))
665 // store the order of linkedIds
666 if (!linkedOrder.contains(linkedId))
667 linkedOrder.add(linkedId);
670 // store arg in the list of args used
671 if (!argList.contains(a))
675 private String defaultLinkedId(boolean increment)
677 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
678 .append(Integer.toString(defaultLinkedIdCounter)).toString();
681 while (linkedArgs.containsKey(defaultLinkedId))
683 defaultLinkedIdCounter++;
684 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
685 .append(Integer.toString(defaultLinkedIdCounter))
689 getOrCreateLinkedArgValuesMap(defaultLinkedId);
690 return defaultLinkedId;
693 public String makeSubstitutions(String val, String linkedId)
695 if (!this.substitutions || val == null)
700 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
702 int closeBracket = val.indexOf(']');
703 if (val.length() == closeBracket)
705 subvals = val.substring(0, closeBracket + 1);
706 rest = val.substring(closeBracket + 1);
713 if (rest.contains(LINKEDIDAUTOCOUNTER))
714 rest = rest.replace(LINKEDIDAUTOCOUNTER,
715 String.valueOf(linkedIdAutoCounter));
716 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
717 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
718 String.valueOf(++linkedIdAutoCounter));
719 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
720 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
721 String.valueOf(defaultLinkedIdCounter));
722 ArgValuesMap avm = linkedArgs.get(linkedId);
725 if (rest.contains(LINKEDIDBASENAME))
727 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
729 if (rest.contains(LINKEDIDEXTENSION))
731 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
733 if (rest.contains(LINKEDIDDIRNAME))
735 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
740 if (rest.contains(ARGFILEBASENAME))
742 rest = rest.replace(ARGFILEBASENAME,
743 FileUtils.getBasename(new File(argFile)));
745 if (rest.contains(ARGFILEDIRNAME))
747 rest = rest.replace(ARGFILEDIRNAME,
748 FileUtils.getDirname(new File(argFile)));
752 return new StringBuilder(subvals).append(rest).toString();
756 * A helper method to take a list of String args where we're expecting
757 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
758 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
759 * "file2", "file3"} *and remove these from the original list object* so that
760 * processing can continue from where it has left off, e.g. args has become
761 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
762 * carries on from the next --arg if available.
764 protected static List<String> getShellGlobbedFilenameValues(Arg a,
765 List<String> args, int i)
767 List<String> vals = new ArrayList<>();
768 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
770 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
771 if (!a.hasOption(Opt.GLOB))
777 public BootstrapArgs getBootstrapArgs()
779 return bootstrapArgs;
782 public boolean isSet(Arg a)
784 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
787 public boolean isSetAtAll(Arg a)
789 for (String linkedId : linkedOrder)
791 if (isSet(linkedId, a))
797 public boolean isSet(String linkedId, Arg a)
799 ArgValuesMap avm = linkedArgs.get(linkedId);
800 return avm == null ? false : avm.containsArg(a);
803 public boolean getBoolean(Arg a)
805 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
807 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
810 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
813 public boolean getBool(String linkedId, Arg a)
815 ArgValuesMap avm = linkedArgs.get(linkedId);
817 return a.getDefaultBoolValue();
818 ArgValues avs = avm.getArgValues(a);
819 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
822 public List<String> getLinkedIds()
827 public ArgValuesMap getLinkedArgs(String id)
829 return linkedArgs.get(id);
833 public String toString()
835 StringBuilder sb = new StringBuilder();
836 sb.append("UNLINKED\n");
837 sb.append(argValuesMapToString(linkedArgs.get(null)));
838 if (getLinkedIds() != null)
840 sb.append("LINKED\n");
841 for (String id : getLinkedIds())
843 // already listed these as UNLINKED args
847 ArgValuesMap avm = getLinkedArgs(id);
848 sb.append("ID: '").append(id).append("'\n");
849 sb.append(argValuesMapToString(avm));
852 return sb.toString();
855 private static String argValuesMapToString(ArgValuesMap avm)
859 StringBuilder sb = new StringBuilder();
860 for (Arg a : avm.getArgKeys())
862 ArgValues v = avm.getArgValues(a);
863 sb.append(v.toString());
866 return sb.toString();
869 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
870 boolean initsubstitutions, BootstrapArgs bsa)
872 List<File> argFiles = new ArrayList<>();
874 for (String pattern : argFilenameGlobs)
876 // I don't think we want to dedup files, making life easier
877 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
880 return parseArgFileList(argFiles, initsubstitutions, bsa);
883 public static ArgParser parseArgFileList(List<File> argFiles,
884 boolean initsubstitutions, BootstrapArgs bsa)
886 List<String> argsList = new ArrayList<>();
887 for (File argFile : argFiles)
889 if (!argFile.exists())
891 String message = Arg.ARGFILE.argString() + EQUALS + "\""
892 + argFile.getPath() + "\": File does not exist.";
893 Jalview.exit(message, 2);
897 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
898 .append(EQUALS).append(argFile.getCanonicalPath())
900 argsList.add(setargfile);
901 argsList.addAll(readArgFile(argFile));
902 argsList.add(Arg.UNSETARGFILE.argString());
903 } catch (IOException e)
905 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
906 + "\": File could not be read.";
907 Jalview.exit(message, 3);
910 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
912 return new ArgParser(argsList, initsubstitutions, true, bsa);
915 protected static List<String> readArgFile(File argFile)
917 List<String> args = new ArrayList<>();
918 if (argFile != null && argFile.exists())
922 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
924 if (line != null && line.length() > 0
925 && line.charAt(0) != ARGFILECOMMENT)
928 } catch (IOException e)
930 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
931 + "\": File could not be read.";
932 Console.debug(message, e);
933 Jalview.exit(message, 3);
939 public static enum Position
945 * get from following Arg of type a or subval of same name (lowercase)
947 public static String getValueFromSubValOrArg(ArgValuesMap avm,
948 ArgValue av, Arg a, SubVals sv)
950 return getFromSubValArgOrPref(avm, av, a, sv, null, null, null);
954 * get from following Arg of type a or subval key or preference pref or
957 public static String getFromSubValArgOrPref(ArgValuesMap avm, ArgValue av,
958 Arg a, SubVals sv, String key, String pref, String def)
960 return getFromSubValArgOrPref(avm, a, Position.AFTER, av, sv, key, pref,
965 * get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
966 * Arg of type a or subval key or preference pref or default def
968 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
969 Position pos, ArgValue av, SubVals sv, String key, String pref,
972 return getFromSubValArgOrPrefWithSubstitutions(null, avm, a, pos, av,
976 public static String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap,
977 ArgValuesMap avm, Arg a, Position pos, ArgValue av, SubVals sv,
978 String key, String pref, String def)
983 if (sv != null && sv.has(key) && sv.get(key) != null)
985 value = ap == null ? sv.get(key)
986 : sv.getWithSubstitutions(ap, avm.getLinkedId(), key);
988 else if (avm != null && avm.containsArg(a))
990 if (pos == Position.FIRST && avm.getValue(a) != null)
991 value = avm.getValue(a);
992 else if (pos == Position.BEFORE
993 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
994 value = avm.getClosestPreviousArgValueOfArg(av, a).getValue();
995 else if (pos == Position.AFTER
996 && avm.getClosestNextArgValueOfArg(av, a) != null)
997 value = avm.getClosestNextArgValueOfArg(av, a).getValue();
999 // look for allstructures subval for Type.STRUCTURE*
1000 Arg arg = av.getArg();
1001 if (value == null && arg.hasOption(Opt.PRIMARY)
1002 && arg.getType() == Type.STRUCTURE
1003 && !a.hasOption(Opt.PRIMARY) && (a.getType() == Type.STRUCTURE
1004 || a.getType() == Type.STRUCTUREIMAGE))
1006 ArgValue av2 = avm.getArgValueOfArgWithSubValKey(a,
1007 Arg.ALLSTRUCTURES.getName());
1010 value = av2.getValue();
1016 value = pref != null ? Cache.getDefault(pref, def) : def;
1021 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
1024 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
1027 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
1028 SubVals sv, String key, String pref, boolean def)
1030 return getFromSubValArgOrPref(avm, a, sv, key, pref, def, false);
1033 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
1034 SubVals sv, String key, String pref, boolean def,
1037 if ((key == null && a == null) || (sv == null && a == null))
1040 boolean usingArgKey = false;
1047 String nokey = ArgParser.NEGATESTRING + key;
1049 // look for key or nokey in subvals first (if using Arg check options)
1052 // check for true boolean
1053 if (sv.has(key) && sv.get(key) != null)
1057 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
1060 "Looking for boolean in subval from non-boolean/non-unary Arg "
1065 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
1068 // check for negative boolean (subval "no..." will be "true")
1069 if (sv.has(nokey) && sv.get(nokey) != null)
1073 if (!(a.hasOption(Opt.BOOLEAN)))
1076 "Looking for negative boolean in subval from non-boolean Arg "
1081 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
1086 if (avm != null && avm.containsArg(a))
1087 return avm.getBoolean(a);
1089 // return preference or default
1090 boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false;
1091 return pref != null ? (invertPref ? !prefVal : prefVal) : def;
1094 // the following methods look for the "*" linkedId and add the argvalue to all
1095 // linkedId ArgValues if it does.
1097 * This version inserts the subvals sv into all created values
1099 private void addValue(String linkedId, Type type, ArgValues avs,
1100 SubVals sv, String v, int argIndex, boolean doSubs)
1102 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
1106 private void addValue(String linkedId, Type type, ArgValues avs, String v,
1107 int argIndex, boolean doSubs)
1109 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, null, v, false,
1113 private void setBoolean(String linkedId, Type type, ArgValues avs,
1114 boolean b, int argIndex)
1116 this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
1117 b, argIndex, false);
1120 private void setNegated(String linkedId, ArgValues avs, boolean b)
1122 this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
1126 private void incrementCount(String linkedId, ArgValues avs)
1128 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
1129 null, false, 0, false);
1134 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1137 private void argValueOperation(Op op, String linkedId, Type type,
1138 ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
1141 // default to merge subvals if subvals are provided
1142 argValueOperation(op, linkedId, type, avs, sv, true, v, b, argIndex,
1147 * The following operations look for the "*" and "open*" linkedIds and add the
1148 * argvalue to all appropriate linkedId ArgValues if it does. If subvals are
1149 * supplied, they are inserted into all new set values.
1152 * The ArgParser.Op operation
1154 * The String linkedId from the ArgValuesMap
1156 * The Arg.Type to attach to this ArgValue
1158 * The ArgValues for this linkedId
1160 * Use these SubVals on the ArgValue
1162 * Merge the SubVals with any existing on the value. False will
1163 * replace unless sv is null
1165 * The value of the ArgValue (may contain subvals).
1167 * The boolean value of the ArgValue.
1169 * The argIndex for the ArgValue.
1171 * Whether to perform substitutions on the subvals and value.
1173 private void argValueOperation(Op op, String linkedId, Type type,
1174 ArgValues avs, SubVals sv, boolean merge, String v, boolean b,
1175 int argIndex, boolean doSubs)
1179 List<String> wildcardLinkedIds = null;
1180 if (a.hasOption(Opt.ALLOWMULTIID))
1184 case MATCHALLLINKEDIDS:
1185 wildcardLinkedIds = getLinkedIds();
1187 case MATCHOPENEDLINKEDIDS:
1188 wildcardLinkedIds = this.storedLinkedIds;
1193 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1194 // to storedLinkedIds
1195 if (linkedId != null && wildcardLinkedIds == null
1196 && a.hasOption(Opt.STORED)
1197 && !storedLinkedIds.contains(linkedId))
1199 storedLinkedIds.add(linkedId);
1202 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1204 if (wildcardLinkedIds != null)
1206 for (String id : wildcardLinkedIds)
1208 // skip incorrectly stored wildcard ids!
1209 if (id == null || MATCHALLLINKEDIDS.equals(id)
1210 || MATCHOPENEDLINKEDIDS.equals(id))
1214 ArgValuesMap avm = linkedArgs.get(id);
1215 // don't set an output if there isn't an input
1216 if (a.hasOption(Opt.REQUIREINPUT)
1217 && !avm.hasArgWithOption(Opt.INPUT))
1220 ArgValues tavs = avm.getOrCreateArgValues(a);
1230 sv = new SubVals(sv, val, merge);
1231 val = makeSubstitutions(sv.getContent(), id);
1233 tavs.addValue(sv, type, val, argIndex, true);
1239 val = makeSubstitutions(v, id);
1241 tavs.addValue(type, val, argIndex, true);
1243 finaliseStoringArgValue(id, tavs);
1247 tavs.setBoolean(type, b, argIndex, true);
1248 finaliseStoringArgValue(id, tavs);
1252 tavs.setNegated(b, true);
1255 case INCREMENTCOUNT:
1256 tavs.incrementCount();
1266 else // no wildcard linkedId -- do it simpler
1276 val = makeSubstitutions(v, linkedId);
1277 sv = new SubVals(sv, val);
1279 avs.addValue(sv, type, val, argIndex, false);
1285 val = makeSubstitutions(v, linkedId);
1287 avs.addValue(type, val, argIndex, false);
1289 finaliseStoringArgValue(linkedId, avs);
1293 avs.setBoolean(type, b, argIndex, false);
1294 finaliseStoringArgValue(linkedId, avs);
1298 avs.setNegated(b, false);
1301 case INCREMENTCOUNT:
1302 avs.incrementCount();
1311 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1313 if (linkedArgs.containsKey(linkedId)
1314 && linkedArgs.get(linkedId) != null)
1315 return linkedArgs.get(linkedId);
1317 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1318 return linkedArgs.get(linkedId);