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;
36 import jalview.bin.Console;
37 import jalview.bin.Jalview;
38 import jalview.bin.Jalview.ExitCode;
39 import jalview.bin.argparser.Arg.Opt;
40 import jalview.bin.argparser.Arg.Type;
41 import jalview.util.FileUtils;
42 import jalview.util.HttpUtils;
44 public class ArgParser
46 protected static final String SINGLEDASH = "-";
48 protected static final String DOUBLEDASH = "--";
50 public static final char EQUALS = '=';
52 public static final String STDOUTFILENAME = "-";
54 protected static final String NEGATESTRING = "no";
57 * the default linked id prefix used for no id (ie when not even square braces
60 protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
63 * the linkedId string used to match all linkedIds seen so far
65 protected static final String MATCHALLLINKEDIDS = "*";
68 * the linkedId string used to match all of the last --open'ed linkedIds
70 protected static final String MATCHOPENEDLINKEDIDS = "open*";
73 * the counter added to the default linked id prefix
75 private int defaultLinkedIdCounter = 0;
78 * the substitution string used to use the defaultLinkedIdCounter
80 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
83 * the linked id prefix used for --open files. NOW the same as DEFAULT
85 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
88 * the counter used for {n} substitutions
90 private int linkedIdAutoCounter = 0;
93 * the linked id substitution string used to increment the idCounter (and use
94 * the incremented value)
96 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
99 * the linked id substitution string used to use the idCounter
101 private static final String LINKEDIDAUTOCOUNTER = "{n}";
104 * the linked id substitution string used to use the filename extension of
107 private static final String LINKEDIDEXTENSION = "{extension}";
110 * the linked id substitution string used to use the base filename of --append
113 private static final String LINKEDIDBASENAME = "{basename}";
116 * the linked id substitution string used to use the dir path of --append or
119 private static final String LINKEDIDDIRNAME = "{dirname}";
122 * On-the-fly substitution (not made at argument parsing time)! the current
123 * structure filename extension
125 private static final String STRUCTUREEXTENSION = "{structureextension}";
128 * On-the-fly substitution (not made at argument parsing time)! the current
129 * structure filename base
131 private static final String STRUCTUREBASENAME = "{structurebasename}";
134 * On-the-fly substitution (not made at argument parsing time)! the current
135 * structure filename dir path
137 private static final String STRUCTUREDIRNAME = "{structuredirname}";
140 * On-the-fly substitution (not made at argument parsing time)! increment the
141 * on-the-fly counter and substitute the incremented value
143 private static final String INCREMENTONTHEFLYCOUNTER = "{++m}";
146 * On-the-fly substitution (not made at argument parsing time)! the current
147 * substitute with the on-the-fly counter
149 private static final String ONTHEFLYCOUNTER = "{m}";
152 * the string used for on-the-fly structure filename substitutions
154 private String currentStructureFilename = null;
157 * the counter used for on-the-fly {m} substitutions
159 private int ontheflyCounter = 0;
162 * the current argfile
164 private String argFile = null;
167 * the linked id substitution string used to use the dir path of the latest
169 /** --argfile name */
170 private static final String ARGFILEBASENAME = "{argfilebasename}";
173 * the linked id substitution string used to use the dir path of the latest
176 private static final String ARGFILEDIRNAME = "{argfiledirname}";
179 * flag to say whether {n} subtitutions in output filenames should be made.
180 * Turn on and off with --substitutions and --nosubstitutions Start with it on
182 private boolean substitutions = true;
185 * flag to say whether the default linkedId is the current default linked id
189 private boolean allLinkedIds = false;
192 * flag to say whether the structure arguments should be applied to all
193 * structures with this linked id
195 private boolean allStructures = false;
197 protected static final Map<String, Arg> argMap;
199 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
201 protected List<String> linkedOrder = new ArrayList<>();
203 protected List<String> storedLinkedIds = new ArrayList<>();
205 protected List<Arg> argList = new ArrayList<>();
207 private static final char ARGFILECOMMENT = '#';
209 private int argIndex = 0;
211 private BootstrapArgs bootstrapArgs = null;
213 private boolean oldArguments = false;
215 private boolean mixedArguments = false;
218 * saved examples of mixed arguments
220 private String[] mixedExamples = new String[] { null, null };
224 argMap = new HashMap<>();
225 for (Arg a : EnumSet.allOf(Arg.class))
227 for (String argName : a.getNames())
229 if (argMap.containsKey(argName))
231 Console.warn("Trying to add argument name multiple times: '"
233 if (argMap.get(argName) != a)
236 "Trying to add argument name multiple times for different Args: '"
237 + argMap.get(argName).getName() + ":" + argName
238 + "' and '" + a.getName() + ":" + argName
243 argMap.put(argName, a);
248 public ArgParser(String[] args)
250 this(args, false, null);
253 public ArgParser(String[] args, boolean initsubstitutions,
257 * Make a mutable new ArrayList so that shell globbing parser works.
258 * (When shell file globbing is used, there are a sequence of non-Arg
259 * arguments (which are the expanded globbed filenames) that need to be
260 * consumed by the --append/--argfile/etc Arg which is most easily done
261 * by removing these filenames from the list one at a time. This can't be
262 * done with an ArrayList made with only Arrays.asList(String[] args) as
263 * that is not mutable. )
265 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
269 public ArgParser(List<String> args, boolean initsubstitutions)
271 this(args, initsubstitutions, false, null);
274 public ArgParser(List<String> args, boolean initsubstitutions,
275 boolean allowPrivate, BootstrapArgs bsa)
277 // do nothing if there are no "--" args and (some "-" args || >0 arg is
281 for (String arg : args)
283 if (arg.startsWith(DOUBLEDASH))
286 if (mixedExamples[1] == null)
288 mixedExamples[1] = arg;
291 else if ((arg.startsWith("-") && !arg.equals(STDOUTFILENAME))
292 || arg.equals("open"))
295 if (mixedExamples[0] == null)
297 mixedExamples[0] = arg;
305 mixedArguments = true;
313 if (oldArguments || mixedArguments)
315 // leave it to the old style -- parse an empty list
316 parse(new ArrayList<String>(), false, false);
322 this.bootstrapArgs = bsa;
326 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
328 parse(args, initsubstitutions, allowPrivate);
331 private void parse(List<String> args, boolean initsubstitutions,
332 boolean allowPrivate)
334 this.substitutions = initsubstitutions;
337 * If the first argument does not start with "--" or "-" or is not "open",
338 * and is a filename that exists or a URL, it is probably a file/list of
339 * files to open so we insert an Arg.OPEN argument before it. This will
340 * mean the list of files at the start of the arguments are all opened
345 String arg0 = args.get(0);
347 && (!arg0.startsWith(DOUBLEDASH) && !arg0.startsWith("-")
348 && !arg0.equals("open") && (new File(arg0).exists()
349 || HttpUtils.startsWithHttpOrHttps(arg0))))
351 // insert "--open" at the start
352 args.add(0, Arg.OPEN.argString());
356 for (int i = 0; i < args.size(); i++)
358 String arg = args.get(i);
360 // look for double-dash, e.g. --arg
361 if (arg.startsWith(DOUBLEDASH))
363 String argName = null;
365 List<String> globVals = null; // for Opt.GLOB only
366 SubVals globSubVals = null; // also for use by Opt.GLOB only
367 String linkedId = null;
370 // look for equals e.g. --arg=value
371 int equalPos = arg.indexOf(EQUALS);
374 argName = arg.substring(DOUBLEDASH.length(), equalPos);
375 val = arg.substring(equalPos + 1);
379 argName = arg.substring(DOUBLEDASH.length());
382 // look for linked ID e.g. --arg[linkedID]
383 int idOpen = argName.indexOf('[');
384 int idClose = argName.indexOf(']');
385 if (idOpen > -1 && idClose == argName.length() - 1)
387 linkedId = argName.substring(idOpen + 1, idClose);
388 argName = argName.substring(0, idOpen);
391 // look for type modification e.g. --help-opening
392 int dashPos = argName.indexOf(SINGLEDASH);
395 String potentialArgName = argName.substring(0, dashPos);
396 Arg potentialArg = argMap.get(potentialArgName);
397 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
399 String typeName = argName.substring(dashPos + 1);
402 type = Type.valueOf(typeName);
403 } catch (IllegalArgumentException e)
407 argName = argName.substring(0, dashPos);
411 Arg a = argMap.get(argName);
412 // check for boolean prepended by "no" e.g. --nowrap
413 boolean negated = false;
416 if (argName.startsWith(NEGATESTRING) && argMap
417 .containsKey(argName.substring(NEGATESTRING.length())))
419 argName = argName.substring(NEGATESTRING.length());
420 a = argMap.get(argName);
425 // after all other args, look for Opt.PREFIXKEV args if still not
427 for (Arg potentialArg : EnumSet.allOf(Arg.class))
429 if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null
430 && argName.startsWith(potentialArg.getName())
433 val = argName.substring(potentialArg.getName().length())
435 argName = argName.substring(0,
436 potentialArg.getName().length());
444 // check for config errors
448 Console.error("Argument '" + arg + "' not recognised. Exiting.");
450 "Invalid argument used." + System.lineSeparator() + "Use"
451 + System.lineSeparator() + "jalview "
452 + Arg.HELP.argString() + System.lineSeparator()
453 + "for a usage statement.",
454 ExitCode.INVALID_ARGUMENT);
457 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
460 "Argument '" + a.argString() + "' is private. Ignoring.");
463 if (!a.hasOption(Opt.BOOLEAN) && negated)
465 // used "no" with a non-boolean option
466 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
467 + "' not a boolean option. Ignoring.");
470 if (!a.hasOption(Opt.STRING) && equalPos > -1)
472 // set --argname=value when arg does not accept values
473 Console.error("Argument '" + a.argString()
474 + "' does not expect a value (given as '" + arg
478 if (!a.hasOption(Opt.LINKED) && linkedId != null)
480 // set --argname[linkedId] when arg does not use linkedIds
481 Console.error("Argument '" + a.argString()
482 + "' does not expect a linked id (given as '" + arg
488 if (a.hasOption(Opt.STRING))
492 if (a.hasOption(Opt.GLOB))
494 // strip off and save the SubVals to be added individually later
495 globSubVals = new SubVals(val);
496 // make substitutions before looking for files
497 String fileGlob = makeSubstitutions(globSubVals.getContent(),
499 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
503 // val is already set -- will be saved in the ArgValue later in
509 // There is no "=" so value is next arg or args (possibly shell
511 if (i + 1 >= args.size())
513 // no value to take for arg, which wants a value
514 Console.error("Argument '" + a.getName()
515 + "' requires a value, none given. Ignoring.");
518 // deal with bash globs here (--arg val* is expanded before reaching
519 // the JVM). Note that SubVals cannot be used in this case.
520 // If using the --arg=val then the glob is preserved and Java globs
521 // will be used later. SubVals can be used.
522 if (a.hasOption(Opt.GLOB))
524 // if this is the first argument with a file list at the start of
525 // the args we add filenames from index i instead of i+1
526 globVals = getShellGlobbedFilenameValues(a, args, i + 1);
530 val = args.get(i + 1);
535 // make NOACTION adjustments
536 // default and auto counter increments
539 linkedIdAutoCounter++;
541 else if (a == Arg.SUBSTITUTIONS)
543 substitutions = !negated;
545 else if (a == Arg.SETARGFILE)
549 else if (a == Arg.UNSETARGFILE)
553 else if (a == Arg.ALL)
555 allLinkedIds = !negated;
557 else if (a == Arg.ALLSTRUCTURES)
559 allStructures = !negated;
562 if (a.hasOption(Opt.STORED))
564 // reset the lastOpenedLinkedIds list
565 this.storedLinkedIds = new ArrayList<>();
568 // this is probably only Arg.NEW and Arg.OPEN
569 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
571 // use the next default prefixed OPENLINKEDID
572 defaultLinkedId(true);
575 String autoCounterString = null;
576 String defaultLinkedId = defaultLinkedId(false);
577 boolean usingDefaultLinkedId = false;
578 if (a.hasOption(Opt.LINKED))
580 if (linkedId == null)
582 if (a.hasOption(Opt.OUTPUTFILE) && a.hasOption(Opt.ALLOWMULTIID)
583 && val.contains(MATCHALLLINKEDIDS))
585 // --output=*.ext is shorthand for --output {basename}.ext
586 // --output=*/*.ext is shorthand for
587 // --output {dirname}/{basename}.ext
588 // (or --image=*.ext)
589 linkedId = allLinkedIds ? MATCHALLLINKEDIDS
590 : MATCHOPENEDLINKEDIDS;
591 val = FileUtils.convertWildcardsToPath(val, MATCHALLLINKEDIDS,
592 LINKEDIDDIRNAME, LINKEDIDBASENAME);
594 else if (allLinkedIds && a.hasOption(Opt.ALLOWMULTIID))
596 linkedId = MATCHALLLINKEDIDS;
598 else if (a.hasOption(Opt.ALLOWMULTIID)
599 && this.storedLinkedIds != null
600 && this.storedLinkedIds.size() > 0)
602 linkedId = MATCHOPENEDLINKEDIDS;
606 // use default linkedId for linked arguments
607 linkedId = defaultLinkedId;
608 usingDefaultLinkedId = true;
609 Console.debug("Changing linkedId to '" + linkedId + "' from "
615 if (linkedId.contains(LINKEDIDAUTOCOUNTER))
617 // turn {n} to the autoCounter
618 autoCounterString = Integer.toString(linkedIdAutoCounter);
619 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
621 Console.debug("Changing linkedId to '" + linkedId + "' from "
624 if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
626 // turn {++n} to the incremented autoCounter
627 autoCounterString = Integer.toString(++linkedIdAutoCounter);
628 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
630 Console.debug("Changing linkedId to '" + linkedId + "' from "
636 // do not continue in this block for NOACTION args
637 if (a.hasOption(Opt.NOACTION))
640 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
642 // not dealing with both NODUPLICATEVALUES and GLOB
643 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
645 Console.error("Argument '" + a.argString()
646 + "' cannot contain a duplicate value ('" + val
647 + "'). Ignoring this and subsequent occurrences.");
651 // check for unique id
652 SubVals subvals = new SubVals(val);
653 boolean addNewSubVals = false;
654 String id = subvals.get(ArgValues.ID);
655 if (id != null && avm.hasId(a, id))
657 Console.error("Argument '" + a.argString()
658 + "' has a duplicate id ('" + id + "'). Ignoring.");
662 // set allstructures to all non-primary structure options in this linked
663 // id if --allstructures has been set
664 if (allStructures && (a.getType() == Type.STRUCTURE
665 // || a.getType() == Type.STRUCTUREIMAGE)
666 ) && !a.hasOption(Opt.PRIMARY))
668 if (!subvals.has(Arg.ALLSTRUCTURES.getName()))
669 // && !subvals.has("structureid"))
671 subvals.put(Arg.ALLSTRUCTURES.getName(), "true");
672 addNewSubVals = true;
676 ArgValues avs = avm.getOrCreateArgValues(a);
678 // store appropriate String value(s)
679 if (a.hasOption(Opt.STRING))
681 if (a.hasOption(Opt.GLOB) && globVals != null
682 && globVals.size() > 0)
684 Enumeration<String> gve = Collections.enumeration(globVals);
685 while (gve.hasMoreElements())
687 String v = gve.nextElement();
688 SubVals vsv = new SubVals(globSubVals, v);
689 addValue(linkedId, type, avs, vsv, v, argIndex++, true);
690 // if we're using defaultLinkedId and the arg increments the
692 if (gve.hasMoreElements() && usingDefaultLinkedId
693 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
695 // increment the default linkedId
696 linkedId = defaultLinkedId(true);
697 // get new avm and avs
698 avm = linkedArgs.get(linkedId);
699 avs = avm.getOrCreateArgValues(a);
705 // addValue(linkedId, type, avs, val, argIndex, true);
706 addValue(linkedId, type, avs, addNewSubVals ? subvals : null,
707 val, argIndex, true);
710 else if (a.hasOption(Opt.BOOLEAN))
712 setBoolean(linkedId, type, avs, !negated, argIndex);
713 setNegated(linkedId, avs, negated);
715 else if (a.hasOption(Opt.UNARY))
717 setBoolean(linkedId, type, avs, true, argIndex);
720 // remove the '*' or 'open*' linkedId that should be empty if it was
722 if ((MATCHALLLINKEDIDS.equals(linkedId)
723 || MATCHOPENEDLINKEDIDS.equals(linkedId))
724 && linkedArgs.containsKey(linkedId))
726 linkedArgs.remove(linkedId);
732 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
735 incrementCount(linkedId, avs);
738 // store in appropriate place
739 if (a.hasOption(Opt.LINKED))
741 // store the order of linkedIds
742 if (!linkedOrder.contains(linkedId))
743 linkedOrder.add(linkedId);
746 // store arg in the list of args used
747 if (!argList.contains(a))
751 private String defaultLinkedId(boolean increment)
753 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
754 .append(Integer.toString(defaultLinkedIdCounter)).toString();
757 while (linkedArgs.containsKey(defaultLinkedId))
759 defaultLinkedIdCounter++;
760 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
761 .append(Integer.toString(defaultLinkedIdCounter))
765 getOrCreateLinkedArgValuesMap(defaultLinkedId);
766 return defaultLinkedId;
769 public String makeSubstitutions(String val, String linkedId)
771 return makeSubstitutions(val, linkedId, false);
774 public String makeSubstitutions(String val, String linkedId,
777 if (!this.substitutions || val == null)
782 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
784 int closeBracket = val.indexOf(']');
785 if (val.length() == closeBracket)
787 subvals = val.substring(0, closeBracket + 1);
788 rest = val.substring(closeBracket + 1);
795 if (rest.contains(LINKEDIDAUTOCOUNTER))
797 rest = rest.replace(LINKEDIDAUTOCOUNTER,
798 String.valueOf(linkedIdAutoCounter));
800 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
802 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
803 String.valueOf(++linkedIdAutoCounter));
805 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
807 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
808 String.valueOf(defaultLinkedIdCounter));
810 ArgValuesMap avm = linkedArgs.get(linkedId);
813 if (rest.contains(LINKEDIDBASENAME))
815 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
817 if (rest.contains(LINKEDIDEXTENSION))
819 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
821 if (rest.contains(LINKEDIDDIRNAME))
823 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
828 if (rest.contains(ARGFILEBASENAME))
830 rest = rest.replace(ARGFILEBASENAME,
831 FileUtils.getBasename(new File(argFile)));
833 if (rest.contains(ARGFILEDIRNAME))
835 rest = rest.replace(ARGFILEDIRNAME,
836 FileUtils.getDirname(new File(argFile)));
841 if (rest.contains(ONTHEFLYCOUNTER))
843 rest = rest.replace(ONTHEFLYCOUNTER,
844 String.valueOf(ontheflyCounter));
846 if (rest.contains(INCREMENTONTHEFLYCOUNTER))
848 rest = rest.replace(INCREMENTONTHEFLYCOUNTER,
849 String.valueOf(++ontheflyCounter));
851 if (currentStructureFilename != null)
853 if (rest.contains(STRUCTUREBASENAME))
855 rest = rest.replace(STRUCTUREBASENAME, FileUtils
856 .getBasename(new File(currentStructureFilename)));
858 if (rest.contains(STRUCTUREDIRNAME))
860 rest = rest.replace(STRUCTUREDIRNAME,
861 FileUtils.getDirname(new File(currentStructureFilename)));
866 return new StringBuilder(subvals).append(rest).toString();
870 * A helper method to take a list of String args where we're expecting
871 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
872 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
873 * "file2", "file3"} *and remove these from the original list object* so that
874 * processing can continue from where it has left off, e.g. args has become
875 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
876 * carries on from the next --arg if available.
878 protected static List<String> getShellGlobbedFilenameValues(Arg a,
879 List<String> args, int i)
881 List<String> vals = new ArrayList<>();
882 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
884 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
885 if (!a.hasOption(Opt.GLOB))
891 public BootstrapArgs getBootstrapArgs()
893 return bootstrapArgs;
896 public boolean isSet(Arg a)
898 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
901 public boolean isSetAtAll(Arg a)
903 for (String linkedId : linkedOrder)
905 if (isSet(linkedId, a))
911 public boolean isSet(String linkedId, Arg a)
913 ArgValuesMap avm = linkedArgs.get(linkedId);
914 return avm == null ? false : avm.containsArg(a);
917 public boolean getBoolean(Arg a)
919 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
921 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
924 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
927 public boolean getBool(String linkedId, Arg a)
929 ArgValuesMap avm = linkedArgs.get(linkedId);
931 return a.getDefaultBoolValue();
932 ArgValues avs = avm.getArgValues(a);
933 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
936 public List<String> getLinkedIds()
941 public ArgValuesMap getLinkedArgs(String id)
943 return linkedArgs.get(id);
947 public String toString()
949 StringBuilder sb = new StringBuilder();
950 sb.append("UNLINKED\n");
951 sb.append(argValuesMapToString(linkedArgs.get(null)));
952 if (getLinkedIds() != null)
954 sb.append("LINKED\n");
955 for (String id : getLinkedIds())
957 // already listed these as UNLINKED args
961 ArgValuesMap avm = getLinkedArgs(id);
962 sb.append("ID: '").append(id).append("'\n");
963 sb.append(argValuesMapToString(avm));
966 return sb.toString();
969 private static String argValuesMapToString(ArgValuesMap avm)
973 StringBuilder sb = new StringBuilder();
974 for (Arg a : avm.getArgKeys())
976 ArgValues v = avm.getArgValues(a);
977 sb.append(v.toString());
980 return sb.toString();
983 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
984 boolean initsubstitutions, BootstrapArgs bsa)
986 List<File> argFiles = new ArrayList<>();
988 for (String pattern : argFilenameGlobs)
990 // I don't think we want to dedup files, making life easier
991 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
994 return parseArgFileList(argFiles, initsubstitutions, bsa);
997 public static ArgParser parseArgFileList(List<File> argFiles,
998 boolean initsubstitutions, BootstrapArgs bsa)
1000 List<String> argsList = new ArrayList<>();
1001 for (File argFile : argFiles)
1003 if (!argFile.exists())
1005 String message = Arg.ARGFILE.argString() + EQUALS + "\""
1006 + argFile.getPath() + "\": File does not exist.";
1007 Jalview.exit(message, ExitCode.FILE_NOT_FOUND);
1011 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
1012 .append(EQUALS).append(argFile.getCanonicalPath())
1014 argsList.add(setargfile);
1015 argsList.addAll(readArgFile(argFile));
1016 argsList.add(Arg.UNSETARGFILE.argString());
1017 } catch (IOException e)
1019 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
1020 + "\": File could not be read.";
1021 Jalview.exit(message, ExitCode.FILE_NOT_READABLE);
1024 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
1026 return new ArgParser(argsList, initsubstitutions, true, bsa);
1029 protected static List<String> readArgFile(File argFile)
1031 List<String> args = new ArrayList<>();
1032 if (argFile != null && argFile.exists())
1036 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
1038 if (line != null && line.length() > 0
1039 && line.charAt(0) != ARGFILECOMMENT)
1042 } catch (IOException e)
1044 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
1045 + "\": File could not be read.";
1046 Console.debug(message, e);
1047 Jalview.exit(message, ExitCode.FILE_NOT_READABLE);
1053 // the following methods look for the "*" linkedId and add the argvalue to all
1054 // linkedId ArgValues if it does.
1056 * This version inserts the subvals sv into all created values
1058 private void addValue(String linkedId, Type type, ArgValues avs,
1059 SubVals sv, String v, int argIndex, boolean doSubs)
1061 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
1065 private void setBoolean(String linkedId, Type type, ArgValues avs,
1066 boolean b, int argIndex)
1068 this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
1069 b, argIndex, false);
1072 private void setNegated(String linkedId, ArgValues avs, boolean b)
1074 this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
1078 private void incrementCount(String linkedId, ArgValues avs)
1080 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
1081 null, false, 0, false);
1086 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1089 private void argValueOperation(Op op, String linkedId, Type type,
1090 ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
1093 // default to merge subvals if subvals are provided
1094 argValueOperation(op, linkedId, type, avs, sv, true, v, b, argIndex,
1099 * The following operations look for the "*" and "open*" linkedIds and add the
1100 * argvalue to all appropriate linkedId ArgValues if it does. If subvals are
1101 * supplied, they are inserted into all new set values.
1104 * The ArgParser.Op operation
1106 * The String linkedId from the ArgValuesMap
1108 * The Arg.Type to attach to this ArgValue
1110 * The ArgValues for this linkedId
1112 * Use these SubVals on the ArgValue
1114 * Merge the SubVals with any existing on the value. False will
1115 * replace unless sv is null
1117 * The value of the ArgValue (may contain subvals).
1119 * The boolean value of the ArgValue.
1121 * The argIndex for the ArgValue.
1123 * Whether to perform substitutions on the subvals and value.
1125 private void argValueOperation(Op op, String linkedId, Type type,
1126 ArgValues avs, SubVals sv, boolean merge, String v, boolean b,
1127 int argIndex, boolean doSubs)
1131 List<String> wildcardLinkedIds = null;
1132 if (a.hasOption(Opt.ALLOWMULTIID))
1136 case MATCHALLLINKEDIDS:
1137 wildcardLinkedIds = getLinkedIds();
1139 case MATCHOPENEDLINKEDIDS:
1140 wildcardLinkedIds = this.storedLinkedIds;
1145 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1146 // to storedLinkedIds
1147 if (linkedId != null && wildcardLinkedIds == null
1148 && a.hasOption(Opt.STORED)
1149 && !storedLinkedIds.contains(linkedId))
1151 storedLinkedIds.add(linkedId);
1154 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1156 if (wildcardLinkedIds != null)
1158 for (String id : wildcardLinkedIds)
1160 // skip incorrectly stored wildcard ids!
1161 if (id == null || MATCHALLLINKEDIDS.equals(id)
1162 || MATCHOPENEDLINKEDIDS.equals(id))
1166 ArgValuesMap avm = linkedArgs.get(id);
1167 // don't set an output if there isn't an input
1168 if (a.hasOption(Opt.REQUIREINPUT)
1169 && !avm.hasArgWithOption(Opt.INPUT))
1172 ArgValues tavs = avm.getOrCreateArgValues(a);
1182 sv = new SubVals(sv, val, merge);
1183 val = makeSubstitutions(sv.getContent(), id);
1185 tavs.addValue(sv, type, val, argIndex, true);
1191 val = makeSubstitutions(v, id);
1193 tavs.addValue(type, val, argIndex, true);
1195 finaliseStoringArgValue(id, tavs);
1199 tavs.setBoolean(type, b, argIndex, true);
1200 finaliseStoringArgValue(id, tavs);
1204 tavs.setNegated(b, true);
1207 case INCREMENTCOUNT:
1208 tavs.incrementCount();
1218 else // no wildcard linkedId -- do it simpler
1228 val = makeSubstitutions(v, linkedId);
1229 sv = new SubVals(sv, val);
1231 avs.addValue(sv, type, val, argIndex, false);
1237 val = makeSubstitutions(v, linkedId);
1239 avs.addValue(type, val, argIndex, false);
1241 finaliseStoringArgValue(linkedId, avs);
1245 avs.setBoolean(type, b, argIndex, false);
1246 finaliseStoringArgValue(linkedId, avs);
1250 avs.setNegated(b, false);
1253 case INCREMENTCOUNT:
1254 avs.incrementCount();
1263 private ArgValuesMap getOrCreateLinkedArgValuesMap(String linkedId)
1265 if (linkedArgs.containsKey(linkedId)
1266 && linkedArgs.get(linkedId) != null)
1267 return linkedArgs.get(linkedId);
1269 linkedArgs.put(linkedId, new ArgValuesMap(linkedId));
1270 return linkedArgs.get(linkedId);
1273 public boolean isOldStyle()
1275 return oldArguments;
1278 public boolean isMixedStyle()
1280 return mixedArguments;
1283 public String[] getMixedExamples()
1285 return mixedExamples;
1288 public void setStructureFilename(String s)
1290 this.currentStructureFilename = s;