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 * the current argfile
124 private String argFile = null;
127 * the linked id substitution string used to use the dir path of the latest
129 /** --argfile name */
130 private static final String ARGFILEBASENAME = "{argfilebasename}";
133 * the linked id substitution string used to use the dir path of the latest
136 private static final String ARGFILEDIRNAME = "{argfiledirname}";
139 * flag to say whether {n} subtitutions in output filenames should be made.
140 * Turn on and off with --substitutions and --nosubstitutions Start with it on
142 private boolean substitutions = true;
145 * flag to say whether the default linkedId is the current default linked id
149 private boolean allLinkedIds = false;
152 * flag to say whether the structure arguments should be applied to all
153 * structures with this linked id
155 private boolean allStructures = false;
157 protected static final Map<String, Arg> argMap;
159 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
161 protected List<String> linkedOrder = new ArrayList<>();
163 protected List<String> storedLinkedIds = new ArrayList<>();
165 protected List<Arg> argList = new ArrayList<>();
167 private static final char ARGFILECOMMENT = '#';
169 private int argIndex = 0;
171 private BootstrapArgs bootstrapArgs = null;
173 private boolean oldArguments = false;
175 private boolean mixedArguments = false;
178 * saved examples of mixed arguments
180 private String[] mixedExamples = new String[] { null, null };
184 argMap = new HashMap<>();
185 for (Arg a : EnumSet.allOf(Arg.class))
187 for (String argName : a.getNames())
189 if (argMap.containsKey(argName))
191 Console.warn("Trying to add argument name multiple times: '"
193 if (argMap.get(argName) != a)
196 "Trying to add argument name multiple times for different Args: '"
197 + argMap.get(argName).getName() + ":" + argName
198 + "' and '" + a.getName() + ":" + argName
203 argMap.put(argName, a);
208 public ArgParser(String[] args)
210 this(args, false, null);
213 public ArgParser(String[] args, boolean initsubstitutions,
217 * Make a mutable new ArrayList so that shell globbing parser works.
218 * (When shell file globbing is used, there are a sequence of non-Arg
219 * arguments (which are the expanded globbed filenames) that need to be
220 * consumed by the --append/--argfile/etc Arg which is most easily done
221 * by removing these filenames from the list one at a time. This can't be
222 * done with an ArrayList made with only Arrays.asList(String[] args) as
223 * that is not mutable. )
225 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false,
229 public ArgParser(List<String> args, boolean initsubstitutions)
231 this(args, initsubstitutions, false, null);
234 public ArgParser(List<String> args, boolean initsubstitutions,
235 boolean allowPrivate, BootstrapArgs bsa)
237 // do nothing if there are no "--" args and (some "-" args || >0 arg is
241 for (String arg : args)
243 if (arg.startsWith(DOUBLEDASH))
246 if (mixedExamples[1] == null)
248 mixedExamples[1] = arg;
251 else if ((arg.startsWith("-") && !arg.equals(STDOUTFILENAME))
252 || arg.equals("open"))
255 if (mixedExamples[0] == null)
257 mixedExamples[0] = arg;
265 mixedArguments = true;
273 if (oldArguments || mixedArguments)
275 // leave it to the old style -- parse an empty list
276 parse(new ArrayList<String>(), false, false);
281 this.bootstrapArgs = bsa;
283 this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args);
284 parse(args, initsubstitutions, allowPrivate);
287 private void parse(List<String> args, boolean initsubstitutions,
288 boolean allowPrivate)
290 this.substitutions = initsubstitutions;
293 * If the first argument does not start with "--" or "-" or is not "open",
294 * and is a filename that exists or a URL, it is probably a file/list of
295 * files to open so we insert an Arg.OPEN argument before it. This will
296 * mean the list of files at the start of the arguments are all opened
301 String arg0 = args.get(0);
303 && (!arg0.startsWith(DOUBLEDASH) && !arg0.startsWith("-")
304 && !arg0.equals("open") && (new File(arg0).exists()
305 || HttpUtils.startsWithHttpOrHttps(arg0))))
307 // insert "--open" at the start
308 args.add(0, Arg.OPEN.argString());
312 for (int i = 0; i < args.size(); i++)
314 String arg = args.get(i);
316 // look for double-dash, e.g. --arg
317 if (arg.startsWith(DOUBLEDASH))
319 String argName = null;
321 List<String> globVals = null; // for Opt.GLOB only
322 SubVals globSubVals = null; // also for use by Opt.GLOB only
323 String linkedId = null;
326 // look for equals e.g. --arg=value
327 int equalPos = arg.indexOf(EQUALS);
330 argName = arg.substring(DOUBLEDASH.length(), equalPos);
331 val = arg.substring(equalPos + 1);
335 argName = arg.substring(DOUBLEDASH.length());
338 // look for linked ID e.g. --arg[linkedID]
339 int idOpen = argName.indexOf('[');
340 int idClose = argName.indexOf(']');
341 if (idOpen > -1 && idClose == argName.length() - 1)
343 linkedId = argName.substring(idOpen + 1, idClose);
344 argName = argName.substring(0, idOpen);
347 // look for type modification e.g. --help-opening
348 int dashPos = argName.indexOf(SINGLEDASH);
351 String potentialArgName = argName.substring(0, dashPos);
352 Arg potentialArg = argMap.get(potentialArgName);
353 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
355 String typeName = argName.substring(dashPos + 1);
358 type = Type.valueOf(typeName);
359 } catch (IllegalArgumentException e)
363 argName = argName.substring(0, dashPos);
367 Arg a = argMap.get(argName);
368 // check for boolean prepended by "no" e.g. --nowrap
369 boolean negated = false;
372 if (argName.startsWith(NEGATESTRING) && argMap
373 .containsKey(argName.substring(NEGATESTRING.length())))
375 argName = argName.substring(NEGATESTRING.length());
376 a = argMap.get(argName);
381 // after all other args, look for Opt.PREFIXKEV args if still not
383 for (Arg potentialArg : EnumSet.allOf(Arg.class))
385 if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null
386 && argName.startsWith(potentialArg.getName())
389 val = argName.substring(potentialArg.getName().length())
391 argName = argName.substring(0,
392 potentialArg.getName().length());
400 // check for config errors
404 Console.error("Argument '" + arg + "' not recognised. Exiting.");
406 "Invalid argument used." + System.lineSeparator() + "Use"
407 + System.lineSeparator() + "jalview "
408 + Arg.HELP.argString() + System.lineSeparator()
409 + "for a usage statement.",
410 ExitCode.INVALID_ARGUMENT);
413 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
416 "Argument '" + a.argString() + "' is private. Ignoring.");
419 if (!a.hasOption(Opt.BOOLEAN) && negated)
421 // used "no" with a non-boolean option
422 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
423 + "' not a boolean option. Ignoring.");
426 if (!a.hasOption(Opt.STRING) && equalPos > -1)
428 // set --argname=value when arg does not accept values
429 Console.error("Argument '" + a.argString()
430 + "' does not expect a value (given as '" + arg
434 if (!a.hasOption(Opt.LINKED) && linkedId != null)
436 // set --argname[linkedId] when arg does not use linkedIds
437 Console.error("Argument '" + a.argString()
438 + "' does not expect a linked id (given as '" + arg
444 if (a.hasOption(Opt.STRING))
448 if (a.hasOption(Opt.GLOB))
450 // strip off and save the SubVals to be added individually later
451 globSubVals = new SubVals(val);
452 // make substitutions before looking for files
453 String fileGlob = makeSubstitutions(globSubVals.getContent(),
455 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
459 // val is already set -- will be saved in the ArgValue later in
465 // There is no "=" so value is next arg or args (possibly shell
467 if (i + 1 >= args.size())
469 // no value to take for arg, which wants a value
470 Console.error("Argument '" + a.getName()
471 + "' requires a value, none given. Ignoring.");
474 // deal with bash globs here (--arg val* is expanded before reaching
475 // the JVM). Note that SubVals cannot be used in this case.
476 // If using the --arg=val then the glob is preserved and Java globs
477 // will be used later. SubVals can be used.
478 if (a.hasOption(Opt.GLOB))
480 // if this is the first argument with a file list at the start of
481 // the args we add filenames from index i instead of i+1
482 globVals = getShellGlobbedFilenameValues(a, args, i + 1);
486 val = args.get(i + 1);
491 // make NOACTION adjustments
492 // default and auto counter increments
495 linkedIdAutoCounter++;
497 else if (a == Arg.SUBSTITUTIONS)
499 substitutions = !negated;
501 else if (a == Arg.SETARGFILE)
505 else if (a == Arg.UNSETARGFILE)
509 else if (a == Arg.ALL)
511 allLinkedIds = !negated;
513 else if (a == Arg.ALLSTRUCTURES)
515 allStructures = !negated;
518 if (a.hasOption(Opt.STORED))
520 // reset the lastOpenedLinkedIds list
521 this.storedLinkedIds = new ArrayList<>();
524 // this is probably only Arg.NEW and Arg.OPEN
525 if (a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
527 // use the next default prefixed OPENLINKEDID
528 defaultLinkedId(true);
531 String autoCounterString = null;
532 String defaultLinkedId = defaultLinkedId(false);
533 boolean usingDefaultLinkedId = false;
534 if (a.hasOption(Opt.LINKED))
536 if (linkedId == null)
538 if (a.hasOption(Opt.OUTPUTFILE) && a.hasOption(Opt.ALLOWMULTIID)
539 && val.contains(MATCHALLLINKEDIDS))
541 // --output=*.ext is shorthand for --output {basename}.ext
542 // --output=*/*.ext is shorthand for
543 // --output {dirname}/{basename}.ext
544 // (or --image=*.ext)
545 linkedId = allLinkedIds ? MATCHALLLINKEDIDS
546 : MATCHOPENEDLINKEDIDS;
547 val = FileUtils.convertWildcardsToPath(val, MATCHALLLINKEDIDS,
548 LINKEDIDDIRNAME, LINKEDIDBASENAME);
550 else if (allLinkedIds && a.hasOption(Opt.ALLOWMULTIID))
552 linkedId = MATCHALLLINKEDIDS;
554 else if (a.hasOption(Opt.ALLOWMULTIID)
555 && this.storedLinkedIds != null
556 && this.storedLinkedIds.size() > 0)
558 linkedId = MATCHOPENEDLINKEDIDS;
562 // use default linkedId for linked arguments
563 linkedId = defaultLinkedId;
564 usingDefaultLinkedId = true;
565 Console.debug("Changing linkedId to '" + linkedId + "' from "
571 if (linkedId.contains(LINKEDIDAUTOCOUNTER))
573 // turn {n} to the autoCounter
574 autoCounterString = Integer.toString(linkedIdAutoCounter);
575 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
577 Console.debug("Changing linkedId to '" + linkedId + "' from "
580 if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
582 // turn {++n} to the incremented autoCounter
583 autoCounterString = Integer.toString(++linkedIdAutoCounter);
584 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
586 Console.debug("Changing linkedId to '" + linkedId + "' from "
592 // do not continue in this block for NOACTION args
593 if (a.hasOption(Opt.NOACTION))
596 ArgValuesMap avm = getOrCreateLinkedArgValuesMap(linkedId);
598 // not dealing with both NODUPLICATEVALUES and GLOB
599 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
601 Console.error("Argument '" + a.argString()
602 + "' cannot contain a duplicate value ('" + val
603 + "'). Ignoring this and subsequent occurrences.");
607 // check for unique id
608 SubVals subvals = new SubVals(val);
609 boolean addNewSubVals = false;
610 String id = subvals.get(ArgValues.ID);
611 if (id != null && avm.hasId(a, id))
613 Console.error("Argument '" + a.argString()
614 + "' has a duplicate id ('" + id + "'). Ignoring.");
618 // set allstructures to all non-primary structure options in this linked
619 // id if --allstructures has been set
620 if (allStructures && (a.getType() == Type.STRUCTURE
621 // || a.getType() == Type.STRUCTUREIMAGE)
622 ) && !a.hasOption(Opt.PRIMARY))
624 if (!subvals.has(Arg.ALLSTRUCTURES.getName()))
625 // && !subvals.has("structureid"))
627 subvals.put(Arg.ALLSTRUCTURES.getName(), "true");
628 addNewSubVals = true;
632 ArgValues avs = avm.getOrCreateArgValues(a);
634 // store appropriate String value(s)
635 if (a.hasOption(Opt.STRING))
637 if (a.hasOption(Opt.GLOB) && globVals != null
638 && globVals.size() > 0)
640 Enumeration<String> gve = Collections.enumeration(globVals);
641 while (gve.hasMoreElements())
643 String v = gve.nextElement();
644 SubVals vsv = new SubVals(globSubVals, v);
645 addValue(linkedId, type, avs, vsv, v, argIndex++, true);
646 // if we're using defaultLinkedId and the arg increments the
648 if (gve.hasMoreElements() && usingDefaultLinkedId
649 && a.hasOption(Opt.INCREMENTDEFAULTCOUNTER))
651 // increment the default linkedId
652 linkedId = defaultLinkedId(true);
653 // get new avm and avs
654 avm = linkedArgs.get(linkedId);
655 avs = avm.getOrCreateArgValues(a);
661 // addValue(linkedId, type, avs, val, argIndex, true);
662 addValue(linkedId, type, avs, addNewSubVals ? subvals : null,
663 val, argIndex, true);
666 else if (a.hasOption(Opt.BOOLEAN))
668 setBoolean(linkedId, type, avs, !negated, argIndex);
669 setNegated(linkedId, avs, negated);
671 else if (a.hasOption(Opt.UNARY))
673 setBoolean(linkedId, type, avs, true, argIndex);
676 // remove the '*' or 'open*' linkedId that should be empty if it was
678 if ((MATCHALLLINKEDIDS.equals(linkedId)
679 || MATCHOPENEDLINKEDIDS.equals(linkedId))
680 && linkedArgs.containsKey(linkedId))
682 linkedArgs.remove(linkedId);
688 private void finaliseStoringArgValue(String linkedId, ArgValues avs)
691 incrementCount(linkedId, avs);
694 // store in appropriate place
695 if (a.hasOption(Opt.LINKED))
697 // store the order of linkedIds
698 if (!linkedOrder.contains(linkedId))
699 linkedOrder.add(linkedId);
702 // store arg in the list of args used
703 if (!argList.contains(a))
707 private String defaultLinkedId(boolean increment)
709 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
710 .append(Integer.toString(defaultLinkedIdCounter)).toString();
713 while (linkedArgs.containsKey(defaultLinkedId))
715 defaultLinkedIdCounter++;
716 defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
717 .append(Integer.toString(defaultLinkedIdCounter))
721 getOrCreateLinkedArgValuesMap(defaultLinkedId);
722 return defaultLinkedId;
725 public String makeSubstitutions(String val, String linkedId)
727 if (!this.substitutions || val == null)
732 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
734 int closeBracket = val.indexOf(']');
735 if (val.length() == closeBracket)
737 subvals = val.substring(0, closeBracket + 1);
738 rest = val.substring(closeBracket + 1);
745 if (rest.contains(LINKEDIDAUTOCOUNTER))
746 rest = rest.replace(LINKEDIDAUTOCOUNTER,
747 String.valueOf(linkedIdAutoCounter));
748 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
749 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
750 String.valueOf(++linkedIdAutoCounter));
751 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
752 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
753 String.valueOf(defaultLinkedIdCounter));
754 ArgValuesMap avm = linkedArgs.get(linkedId);
757 if (rest.contains(LINKEDIDBASENAME))
759 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
761 if (rest.contains(LINKEDIDEXTENSION))
763 rest = rest.replace(LINKEDIDEXTENSION, avm.getExtension());
765 if (rest.contains(LINKEDIDDIRNAME))
767 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
772 if (rest.contains(ARGFILEBASENAME))
774 rest = rest.replace(ARGFILEBASENAME,
775 FileUtils.getBasename(new File(argFile)));
777 if (rest.contains(ARGFILEDIRNAME))
779 rest = rest.replace(ARGFILEDIRNAME,
780 FileUtils.getDirname(new File(argFile)));
784 return new StringBuilder(subvals).append(rest).toString();
788 * A helper method to take a list of String args where we're expecting
789 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
790 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
791 * "file2", "file3"} *and remove these from the original list object* so that
792 * processing can continue from where it has left off, e.g. args has become
793 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
794 * carries on from the next --arg if available.
796 protected static List<String> getShellGlobbedFilenameValues(Arg a,
797 List<String> args, int i)
799 List<String> vals = new ArrayList<>();
800 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
802 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
803 if (!a.hasOption(Opt.GLOB))
809 public BootstrapArgs getBootstrapArgs()
811 return bootstrapArgs;
814 public boolean isSet(Arg a)
816 return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a);
819 public boolean isSetAtAll(Arg a)
821 for (String linkedId : linkedOrder)
823 if (isSet(linkedId, a))
829 public boolean isSet(String linkedId, Arg a)
831 ArgValuesMap avm = linkedArgs.get(linkedId);
832 return avm == null ? false : avm.containsArg(a);
835 public boolean getBoolean(Arg a)
837 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
839 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
842 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
845 public boolean getBool(String linkedId, Arg a)
847 ArgValuesMap avm = linkedArgs.get(linkedId);
849 return a.getDefaultBoolValue();
850 ArgValues avs = avm.getArgValues(a);
851 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
854 public List<String> getLinkedIds()
859 public ArgValuesMap getLinkedArgs(String id)
861 return linkedArgs.get(id);
865 public String toString()
867 StringBuilder sb = new StringBuilder();
868 sb.append("UNLINKED\n");
869 sb.append(argValuesMapToString(linkedArgs.get(null)));
870 if (getLinkedIds() != null)
872 sb.append("LINKED\n");
873 for (String id : getLinkedIds())
875 // already listed these as UNLINKED args
879 ArgValuesMap avm = getLinkedArgs(id);
880 sb.append("ID: '").append(id).append("'\n");
881 sb.append(argValuesMapToString(avm));
884 return sb.toString();
887 private static String argValuesMapToString(ArgValuesMap avm)
891 StringBuilder sb = new StringBuilder();
892 for (Arg a : avm.getArgKeys())
894 ArgValues v = avm.getArgValues(a);
895 sb.append(v.toString());
898 return sb.toString();
901 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
902 boolean initsubstitutions, BootstrapArgs bsa)
904 List<File> argFiles = new ArrayList<>();
906 for (String pattern : argFilenameGlobs)
908 // I don't think we want to dedup files, making life easier
909 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
912 return parseArgFileList(argFiles, initsubstitutions, bsa);
915 public static ArgParser parseArgFileList(List<File> argFiles,
916 boolean initsubstitutions, BootstrapArgs bsa)
918 List<String> argsList = new ArrayList<>();
919 for (File argFile : argFiles)
921 if (!argFile.exists())
923 String message = Arg.ARGFILE.argString() + EQUALS + "\""
924 + argFile.getPath() + "\": File does not exist.";
925 Jalview.exit(message, ExitCode.FILE_NOT_FOUND);
929 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
930 .append(EQUALS).append(argFile.getCanonicalPath())
932 argsList.add(setargfile);
933 argsList.addAll(readArgFile(argFile));
934 argsList.add(Arg.UNSETARGFILE.argString());
935 } catch (IOException e)
937 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
938 + "\": File could not be read.";
939 Jalview.exit(message, ExitCode.FILE_NOT_READABLE);
942 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
944 return new ArgParser(argsList, initsubstitutions, true, bsa);
947 protected static List<String> readArgFile(File argFile)
949 List<String> args = new ArrayList<>();
950 if (argFile != null && argFile.exists())
954 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
956 if (line != null && line.length() > 0
957 && line.charAt(0) != ARGFILECOMMENT)
960 } catch (IOException e)
962 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
963 + "\": File could not be read.";
964 Console.debug(message, e);
965 Jalview.exit(message, ExitCode.FILE_NOT_READABLE);
971 // the following methods look for the "*" linkedId and add the argvalue to all
972 // linkedId ArgValues if it does.
974 * This version inserts the subvals sv into all created values
976 private void addValue(String linkedId, Type type, ArgValues avs,
977 SubVals sv, String v, int argIndex, boolean doSubs)
979 this.argValueOperation(Op.ADDVALUE, linkedId, type, avs, sv, v, false,
983 private void setBoolean(String linkedId, Type type, ArgValues avs,
984 boolean b, int argIndex)
986 this.argValueOperation(Op.SETBOOLEAN, linkedId, type, avs, null, null,
990 private void setNegated(String linkedId, ArgValues avs, boolean b)
992 this.argValueOperation(Op.SETNEGATED, linkedId, null, avs, null, null,
996 private void incrementCount(String linkedId, ArgValues avs)
998 this.argValueOperation(Op.INCREMENTCOUNT, linkedId, null, avs, null,
999 null, false, 0, false);
1004 ADDVALUE, SETBOOLEAN, SETNEGATED, INCREMENTCOUNT
1007 private void argValueOperation(Op op, String linkedId, Type type,
1008 ArgValues avs, SubVals sv, String v, boolean b, int argIndex,
1011 // default to merge subvals if subvals are provided
1012 argValueOperation(op, linkedId, type, avs, sv, true, v, b, argIndex,
1017 * The following operations look for the "*" and "open*" linkedIds and add the
1018 * argvalue to all appropriate linkedId ArgValues if it does. If subvals are
1019 * supplied, they are inserted into all new set values.
1022 * The ArgParser.Op operation
1024 * The String linkedId from the ArgValuesMap
1026 * The Arg.Type to attach to this ArgValue
1028 * The ArgValues for this linkedId
1030 * Use these SubVals on the ArgValue
1032 * Merge the SubVals with any existing on the value. False will
1033 * replace unless sv is null
1035 * The value of the ArgValue (may contain subvals).
1037 * The boolean value of the ArgValue.
1039 * The argIndex for the ArgValue.
1041 * Whether to perform substitutions on the subvals and value.
1043 private void argValueOperation(Op op, String linkedId, Type type,
1044 ArgValues avs, SubVals sv, boolean merge, String v, boolean b,
1045 int argIndex, boolean doSubs)
1049 List<String> wildcardLinkedIds = null;
1050 if (a.hasOption(Opt.ALLOWMULTIID))
1054 case MATCHALLLINKEDIDS:
1055 wildcardLinkedIds = getLinkedIds();
1057 case MATCHOPENEDLINKEDIDS:
1058 wildcardLinkedIds = this.storedLinkedIds;
1063 // if we're not a wildcard linkedId and the arg is marked to be stored, add
1064 // to storedLinkedIds
1065 if (linkedId != null && wildcardLinkedIds == null
1066 && a.hasOption(Opt.STORED)
1067 && !storedLinkedIds.contains(linkedId))
1069 storedLinkedIds.add(linkedId);
1072 // if we are a wildcard linkedId, apply the arg and value to all appropriate
1074 if (wildcardLinkedIds != null)
1076 for (String id : wildcardLinkedIds)
1078 // skip incorrectly stored wildcard ids!
1079 if (id == null || MATCHALLLINKEDIDS.equals(id)
1080 || 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 sv = new SubVals(sv, val, merge);
1101 val = makeSubstitutions(sv.getContent(), id);
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);
1191 public boolean isOldStyle()
1193 return oldArguments;
1196 public boolean isMixedStyle()
1198 return mixedArguments;
1201 public String[] getMixedExamples()
1203 return mixedExamples;