-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- *
- * This file is part of Jalview.
- *
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * Jalview is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.bin;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URLDecoder;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import jalview.util.FileUtils;
-
-public class ArgParser
-{
- private static final String DOUBLEDASH = "--";
-
- private static final String NEGATESTRING = "no";
-
- // the default linked id prefix used for no id (not even square braces)
- private static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
-
- // the counter added to the default linked id prefix
- private int defaultLinkedIdCounter = 0;
-
- // the linked id used to increment the idCounter (and use the incremented
- // value)
- private static final String INCREMENTAUTOCOUNTERLINKEDID = "{++n}";
-
- // the linked id used to use the idCounter
- private static final String AUTOCOUNTERLINKEDID = "{n}";
-
- private int idCounter = 0;
-
- // flag to say whether {n} subtitutions in output filenames should be made.
- // Turn on and off with --subs and --nosubs
- private boolean substitutions = false;
-
- private static enum Opt
- {
- BOOLEAN, STRING, UNARY, MULTI, LINKED, NODUPLICATEVALUES, BOOTSTRAP,
- GLOB, NOACTION, ALLOWSUBSTITUTIONS
- }
-
- public enum Arg
- {
- /*
- NOCALCULATION, NOMENUBAR, NOSTATUS, SHOWOVERVIEW, ANNOTATIONS, COLOUR,
- FEATURES, GROOVY, GROUPS, HEADLESS, JABAWS, NOANNOTATION, NOANNOTATION2,
- NODISPLAY, NOGUI, NONEWS, NOQUESTIONNAIRE, NOSORTBYTREE, NOUSAGESTATS,
- OPEN, OPEN2, PROPS, QUESTIONNAIRE, SETPROP, SORTBYTREE, TREE, VDOC,
- VSESS;
- */
- HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, ANNOTATIONS,
- COLOUR, FEATURES, GROOVY, GROUPS, HEADLESS, JABAWS, ANNOTATION,
- ANNOTATION2, DISPLAY, GUI, NEWS, NOQUESTIONNAIRE, SORTBYTREE,
- USAGESTATS, OPEN, OPEN2, PROPS, QUESTIONNAIRE, SETPROP, TREE, VDOC,
- VSESS, OUTPUT, OUTPUTTYPE, SSANNOTATION, NOTEMPFAC, TEMPFAC,
- TEMPFAC_LABEL, TEMPFAC_DESC, TEMPFAC_SHADING, TITLE, PAEMATRIX, WRAP,
- NOSTRUCTURE, STRUCTURE, IMAGE, QUIT, CLOSE, DEBUG("d"), QUIET("q"),
- ARGFILE, INCREMENT, NPP("n++"), SUBSTITUTIONS, NIL;
-
- static
- {
- HELP.setOptions(Opt.UNARY);
- CALCULATION.setOptions(true, Opt.BOOLEAN); // default "true" implies only
- // expecting "--nocalculation"
- MENUBAR.setOptions(true, Opt.BOOLEAN);
- STATUS.setOptions(true, Opt.BOOLEAN);
- SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED);
- ANNOTATIONS.setOptions(Opt.STRING, Opt.LINKED);
- COLOUR.setOptions(Opt.STRING, Opt.LINKED);
- FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
- Opt.ALLOWSUBSTITUTIONS);
- GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
- Opt.ALLOWSUBSTITUTIONS);
- GROUPS.setOptions(Opt.STRING, Opt.LINKED);
- HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP);
- JABAWS.setOptions(Opt.STRING);
- ANNOTATION.setOptions(true, Opt.BOOLEAN, Opt.LINKED);
- ANNOTATION2.setOptions(true, Opt.BOOLEAN, Opt.LINKED);
- DISPLAY.setOptions(true, Opt.BOOLEAN);
- GUI.setOptions(true, Opt.BOOLEAN);
- NEWS.setOptions(true, Opt.BOOLEAN);
- NOQUESTIONNAIRE.setOptions(Opt.UNARY); // unary as --questionnaire=val
- // expects a string value
- SORTBYTREE.setOptions(true, Opt.BOOLEAN);
- USAGESTATS.setOptions(true, Opt.BOOLEAN);
- OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
- Opt.ALLOWSUBSTITUTIONS);
- OPEN2.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS);
- PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP);
- QUESTIONNAIRE.setOptions(Opt.STRING);
- SETPROP.setOptions(Opt.STRING);
- TREE.setOptions(Opt.STRING);
-
- VDOC.setOptions(Opt.UNARY);
- VSESS.setOptions(Opt.UNARY);
-
- OUTPUT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS);
- OUTPUTTYPE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
-
- SSANNOTATION.setOptions(Opt.BOOLEAN, Opt.LINKED);
- NOTEMPFAC.setOptions(Opt.UNARY, Opt.LINKED);
- TEMPFAC.setOptions(Opt.STRING, Opt.LINKED);
- TEMPFAC_LABEL.setOptions(Opt.STRING, Opt.LINKED);
- TEMPFAC_DESC.setOptions(Opt.STRING, Opt.LINKED);
- TEMPFAC_SHADING.setOptions(Opt.BOOLEAN, Opt.LINKED);
- TITLE.setOptions(Opt.STRING, Opt.LINKED);
- PAEMATRIX.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
- Opt.ALLOWSUBSTITUTIONS);
- NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED);
- STRUCTURE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
- Opt.ALLOWSUBSTITUTIONS);
- WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED);
- IMAGE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS);
- QUIT.setOptions(Opt.UNARY);
- CLOSE.setOptions(Opt.UNARY, Opt.LINKED);
- DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
- QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP);
- ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
- Opt.ALLOWSUBSTITUTIONS);
- INCREMENT.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
- NPP.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
- SUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
- NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION);
- // BOOTSTRAP args are parsed before a full parse of arguments and
- // so are accessible at an earlier stage to (e.g.) set debug log level,
- // provide a props file (that might set log level), run headlessly, read
- // an argfile instead of other args.
- }
-
- private final String[] argNames;
-
- private Opt[] argOptions;
-
- private boolean defaultBoolValue = false;
-
- public String toLongString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append("Arg: ").append(this.name());
- for (String name : getNames())
- {
- sb.append(", '").append(name).append("'");
- }
- sb.append("\nOptions: ");
- boolean first = true;
- for (Opt o : argOptions)
- {
- if (!first)
- {
- sb.append(", ");
- }
- sb.append(o.toString());
- first = false;
- }
- sb.append("\n");
- return sb.toString();
- }
-
- private Arg()
- {
- this(new String[0]);
- }
-
- private Arg(String... names)
- {
- int length = (names == null || names.length == 0
- || (names.length == 1 && names[0] == null)) ? 1
- : names.length + 1;
- this.argNames = new String[length];
- this.argNames[0] = this.getName();
- if (length > 1)
- System.arraycopy(names, 0, this.argNames, 1, names.length);
- }
-
- public String[] getNames()
- {
- return argNames;
- }
-
- public String getName()
- {
- return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
- }
-
- @Override
- public final String toString()
- {
- return getName();
- }
-
- public boolean hasOption(Opt o)
- {
- if (argOptions == null)
- return false;
- for (Opt option : argOptions)
- {
- if (o == option)
- return true;
- }
- return false;
- }
-
- protected void setOptions(Opt... options)
- {
- setOptions(false, options);
- }
-
- protected void setOptions(boolean defaultBoolValue, Opt... options)
- {
- this.defaultBoolValue = defaultBoolValue;
- argOptions = options;
- }
-
- protected boolean getDefaultBoolValue()
- {
- return defaultBoolValue;
- }
- }
-
- public static class ArgValues
- {
- private static final String ID = "id";
-
- private Arg arg;
-
- private int argCount = 0;
-
- private boolean boolValue = false;
-
- private boolean negated = false;
-
- private int boolIndex = -1;
-
- private List<Integer> argsIndexes;
-
- private List<ArgValue> argValueList;
-
- private Map<String, ArgValue> idMap = new HashMap<>();
-
- protected ArgValues(Arg a)
- {
- this.arg = a;
- this.argValueList = new ArrayList<ArgValue>();
- this.boolValue = arg.getDefaultBoolValue();
- }
-
- public Arg arg()
- {
- return arg;
- }
-
- protected int getCount()
- {
- return argCount;
- }
-
- protected void incrementCount()
- {
- argCount++;
- }
-
- protected void setNegated(boolean b)
- {
- this.negated = b;
- }
-
- protected boolean isNegated()
- {
- return this.negated;
- }
-
- protected void setBoolean(boolean b, int i)
- {
- this.boolValue = b;
- this.boolIndex = i;
- }
-
- protected boolean getBoolean()
- {
- return this.boolValue;
- }
-
- @Override
- public String toString()
- {
- if (argValueList == null)
- return null;
- StringBuilder sb = new StringBuilder();
- sb.append(arg.toLongString());
- if (arg.hasOption(Opt.BOOLEAN) || arg.hasOption(Opt.UNARY))
- sb.append("Boolean: ").append(boolValue).append("; Default: ")
- .append(arg.getDefaultBoolValue()).append("; Negated: ")
- .append(negated).append("\n");
- if (arg.hasOption(Opt.STRING))
- {
- sb.append("Values:");
- boolean first = true;
- for (ArgValue av : argValueList)
- {
- String v = av.getValue();
- if (!first)
- sb.append(",");
- sb.append("\n '");
- sb.append(v).append("'");
- first = false;
- }
- sb.append("\n");
- }
- sb.append("Count: ").append(argCount).append("\n");
- return sb.toString();
- }
-
- protected void addValue()
- {
- addValue(null, -1);
- }
-
- protected void addValue(String val, int argIndex)
- {
- addArgValue(new ArgValue(val, argIndex));
- }
-
- protected void addArgValue(ArgValue av)
- {
- if ((!arg.hasOption(Opt.MULTI) && argValueList.size() > 0)
- || (arg.hasOption(Opt.NODUPLICATEVALUES)
- && argValueList.contains(av.getValue())))
- return;
- if (argValueList == null)
- {
- argValueList = new ArrayList<ArgValue>();
- }
- SubVals sv = ArgParser.getSubVals(av.getValue());
- if (sv.has(ID))
- {
- String id = sv.get(ID);
- av.setId(id);
- idMap.put(id, av);
- }
- argValueList.add(av);
- }
-
- protected boolean hasValue(String val)
- {
- return argValueList.contains(val);
- }
-
- protected ArgValue getArgValue()
- {
- if (arg.hasOption(Opt.MULTI))
- Console.warn("Requesting single value for multi value argument");
- return argValueList.size() > 0 ? argValueList.get(0) : null;
- }
-
- protected List<ArgValue> getArgValueList()
- {
- return argValueList;
- }
-
- protected boolean hasId(String id)
- {
- return idMap.containsKey(id);
- }
-
- protected ArgValue getId(String id)
- {
- return idMap.get(id);
- }
- }
-
- // old style
- private List<String> vargs = null;
-
- private boolean isApplet;
-
- // private AppletParams appletParams;
-
- public boolean isApplet()
- {
- return isApplet;
- }
-
- public String nextValue()
- {
- return vargs.remove(0);
- }
-
- public int getSize()
- {
- return vargs.size();
- }
-
- public String getValue(String arg)
- {
- return getValue(arg, false);
- }
-
- public String getValue(String arg, boolean utf8decode)
- {
- int index = vargs.indexOf(arg);
- String dc = null;
- String ret = null;
- if (index != -1)
- {
- ret = vargs.get(index + 1).toString();
- vargs.remove(index);
- vargs.remove(index);
- if (utf8decode && ret != null)
- {
- try
- {
- dc = URLDecoder.decode(ret, "UTF-8");
- ret = dc;
- } catch (Exception e)
- {
- // TODO: log failure to decode
- }
- }
- }
- return ret;
- }
-
- // new style
- private static final Map<String, Arg> argMap;
-
- private Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
-
- private List<String> linkedOrder = null;
-
- private List<Arg> argList;
-
- static
- {
- argMap = new HashMap<>();
- for (Arg a : EnumSet.allOf(Arg.class))
- {
- for (String argName : a.getNames())
- {
- if (argMap.containsKey(argName))
- {
- Console.warn("Trying to add argument name multiple times: '"
- + argName + "'"); // RESTORE THIS WHEN MERGED
- if (argMap.get(argName) != a)
- {
- Console.error(
- "Trying to add argument name multiple times for different Args: '"
- + argMap.get(argName).getName() + ":" + argName
- + "' and '" + a.getName() + ":" + argName
- + "'");
- }
- continue;
- }
- argMap.put(argName, a);
- }
- }
- }
-
- public ArgParser(String[] args)
- {
- // make a mutable new ArrayList so that shell globbing parser works
- this(new ArrayList<>(Arrays.asList(args)));
- }
-
- public ArgParser(List<String> args)
- {
- init(args);
- }
-
- private void init(List<String> args)
- {
- // Enumeration<String> argE = Collections.enumeration(args);
- int argIndex = 0;
- // while (argE.hasMoreElements())
- for (int i = 0; i < args.size(); i++)
- {
- // String arg = argE.nextElement();
- String arg = args.get(i);
- String argName = null;
- String val = null;
- List<String> vals = null; // for Opt.GLOB only
- String linkedId = null;
- if (arg.startsWith(DOUBLEDASH))
- {
- int equalPos = arg.indexOf('=');
- if (equalPos > -1)
- {
- argName = arg.substring(DOUBLEDASH.length(), equalPos);
- val = arg.substring(equalPos + 1);
- }
- else
- {
- argName = arg.substring(DOUBLEDASH.length());
- }
- int idOpen = argName.indexOf('[');
- int idClose = argName.indexOf(']');
-
- if (idOpen > -1 && idClose == argName.length() - 1)
- {
- linkedId = argName.substring(idOpen + 1, idClose);
- argName = argName.substring(0, idOpen);
- }
-
- Arg a = argMap.get(argName);
- // check for boolean prepended by "no"
- boolean negated = false;
- if (a == null && argName.startsWith(NEGATESTRING) && argMap
- .containsKey(argName.substring(NEGATESTRING.length())))
- {
- argName = argName.substring(NEGATESTRING.length());
- a = argMap.get(argName);
- negated = true;
- }
-
- // check for config errors
- if (a == null)
- {
- // arg not found
- Console.error("Argument '" + arg + "' not recognised. Ignoring.");
- continue;
- }
- if (!a.hasOption(Opt.BOOLEAN) && negated)
- {
- // used "no" with a non-boolean option
- Console.error("Argument '--" + NEGATESTRING + argName
- + "' not a boolean option. Ignoring.");
- continue;
- }
- if (!a.hasOption(Opt.STRING) && equalPos > -1)
- {
- // set --argname=value when arg does not accept values
- Console.error("Argument '--" + argName
- + "' does not expect a value (given as '" + arg
- + "'). Ignoring.");
- continue;
- }
- if (!a.hasOption(Opt.LINKED) && linkedId != null)
- {
- // set --argname[linkedId] when arg does not use linkedIds
- Console.error("Argument '--" + argName
- + "' does not expect a linked id (given as '" + arg
- + "'). Ignoring.");
- continue;
- }
-
- if (a.hasOption(Opt.STRING) && equalPos == -1)
- {
- // take next arg as value if required, and '=' was not found
- // if (!argE.hasMoreElements())
- if (i + 1 >= args.size())
- {
- // no value to take for arg, which wants a value
- Console.error("Argument '" + a.getName()
- + "' requires a value, none given. Ignoring.");
- continue;
- }
- // deal with bash globs here (--arg val* is expanded before reaching
- // the JVM). Note that SubVals cannot be used in this case.
- // If using the --arg=val then the glob is preserved and Java globs
- // will be used later. SubVals can be used.
- if (a.hasOption(Opt.GLOB))
- {
- vals.addAll(getShellGlobbedFilenameValues(a, args, i + 1));
- }
- else
- {
- val = args.get(i + 1);
- }
- }
-
- // make NOACTION adjustments
- // default and auto counter increments
- if (a == Arg.INCREMENT)
- {
- defaultLinkedIdCounter++;
- }
- else if (a == Arg.NPP)
- {
- idCounter++;
- }
- else if (a == Arg.SUBSTITUTIONS)
- {
- substitutions = !negated;
- }
-
- String autoCounterString = null;
- boolean usingAutoCounterLinkedId = false;
- String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
- .append(Integer.toString(defaultLinkedIdCounter))
- .toString();
- boolean usingDefaultLinkedId = false;
- if (a.hasOption(Opt.LINKED))
- {
- if (linkedId == null)
- {
- // use default linkedId for linked arguments
- linkedId = defaultLinkedId;
- usingDefaultLinkedId = true;
- Console.debug(
- "Changing linkedId to '" + linkedId + "' from " + arg);
- }
- else if (linkedId.equals(AUTOCOUNTERLINKEDID))
- {
- // turn {n} to the autoCounter
- autoCounterString = Integer.toString(idCounter);
- linkedId = autoCounterString;
- usingAutoCounterLinkedId = true;
- Console.debug(
- "Changing linkedId to '" + linkedId + "' from " + arg);
- }
- else if (linkedId.equals(INCREMENTAUTOCOUNTERLINKEDID))
- {
- // turn {++n} to the incremented autoCounter
- autoCounterString = Integer.toString(++idCounter);
- linkedId = autoCounterString;
- usingAutoCounterLinkedId = true;
- Console.debug(
- "Changing linkedId to '" + linkedId + "' from " + arg);
- }
- }
-
- if (!linkedArgs.containsKey(linkedId))
- linkedArgs.put(linkedId, new ArgValuesMap());
-
- // do not continue for NOACTION args
- if (a.hasOption(Opt.NOACTION))
- continue;
-
- ArgValuesMap avm = linkedArgs.get(linkedId);
-
- // not dealing with both NODUPLICATEVALUES and GLOB
- if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
- {
- Console.error("Argument '--" + argName
- + "' cannot contain a duplicate value ('" + val
- + "'). Ignoring this and subsequent occurrences.");
- continue;
- }
-
- // check for unique id
- SubVals sv = ArgParser.getSubVals(val);
- String id = sv.get(ArgValues.ID);
- if (id != null && avm.hasId(a, id))
- {
- Console.error("Argument '--" + argName + "' has a duplicate id ('"
- + id + "'). Ignoring.");
- continue;
- }
-
- ArgValues avs = avm.getOrCreateArgValues(a);
- if (avs == null)
- {
- avs = new ArgValues(a);
- }
- // store appropriate value
- if (a.hasOption(Opt.STRING))
- {
- if (a.hasOption(Opt.GLOB) && vals != null && vals.size() > 0)
- {
- for (String v : vals)
- {
- avs.addValue(makeSubstitutions(v), argIndex++);
- }
- }
- else
- {
- avs.addValue(makeSubstitutions(val), argIndex);
- }
- }
- else if (a.hasOption(Opt.BOOLEAN))
- {
- avs.setBoolean(!negated, argIndex);
- avs.setNegated(negated);
- }
- else if (a.hasOption(Opt.UNARY))
- {
- avs.setBoolean(true, argIndex);
- }
- avs.incrementCount();
-
- // store in appropriate place
- if (a.hasOption(Opt.LINKED))
- {
- // allow a default linked id for single usage
- if (linkedId == null)
- linkedId = defaultLinkedId;
- // store the order of linkedIds
- if (linkedOrder == null)
- linkedOrder = new ArrayList<>();
- if (!linkedOrder.contains(linkedId))
- linkedOrder.add(linkedId);
- }
-
- // store arg in the list of args used
- if (argList == null)
- argList = new ArrayList<>();
- if (!argList.contains(a))
- argList.add(a);
- }
- }
- }
-
- private String makeSubstitutions(String val)
- {
- if (!this.substitutions)
- return val;
-
- String subvals;
- String rest;
- if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
- {
- int closeBracket = val.indexOf(']');
- if (val.length() == closeBracket)
- return val;
- subvals = val.substring(0, closeBracket + 1);
- rest = val.substring(closeBracket + 1);
- }
- else
- {
- subvals = "";
- rest = val;
- }
- rest.replace(AUTOCOUNTERLINKEDID, String.valueOf(idCounter));
- rest.replace(INCREMENTAUTOCOUNTERLINKEDID, String.valueOf(++idCounter));
- rest.replace("{}", String.valueOf(defaultLinkedIdCounter));
-
- return new StringBuilder(subvals).append(rest).toString();
- }
-
- /*
- * A helper method to take a list of String args where we're expecting
- * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
- * and the index of the globbed arg, here 1. It returns a
- * List<String> {"file1", "file2", "file3"}
- * *and remove these from the original list object* so that processing
- * can continue from where it has left off, e.g. args has become
- * {"--previousargs", "--arg", "--otheroptionsornot"}
- * so the next increment carries on from the next --arg if available.
- */
- private static List<String> getShellGlobbedFilenameValues(Arg a,
- List<String> args, int i)
- {
- List<String> vals = new ArrayList<>();
- while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
- {
- vals.add(args.remove(i));
- if (!a.hasOption(Opt.GLOB))
- break;
- }
- return vals;
- }
-
- public boolean isSet(Arg a)
- {
- return a.hasOption(Opt.LINKED) ? isSet("", a) : isSet(null, a);
- }
-
- public boolean isSet(String linkedId, Arg a)
- {
- ArgValuesMap avm = linkedArgs.get(linkedId);
- return avm == null ? false : avm.containsArg(a);
- }
-
- public boolean getBool(Arg a)
- {
- if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
- {
- Console.warn("Getting boolean from non boolean Arg '" + a.getName()
- + "'.");
- }
- return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
- }
-
- public boolean getBool(String linkedId, Arg a)
- {
- ArgValuesMap avm = linkedArgs.get(linkedId);
- if (avm == null)
- return a.getDefaultBoolValue();
- ArgValues avs = avm.getArgValues(a);
- return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
- }
-
- public List<String> linkedIds()
- {
- return linkedOrder;
- }
-
- public ArgValuesMap linkedArgs(String id)
- {
- return linkedArgs.get(id);
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append("UNLINKED\n");
- sb.append(argValuesMapToString(linkedArgs.get(null)));
- if (linkedIds() != null)
- {
- sb.append("LINKED\n");
- for (String id : linkedIds())
- {
- // already listed these as UNLINKED args
- if (id == null)
- continue;
-
- ArgValuesMap avm = linkedArgs(id);
- sb.append("ID: '").append(id).append("'\n");
- sb.append(argValuesMapToString(avm));
- }
- }
- return sb.toString();
- }
-
- private static String argValuesMapToString(ArgValuesMap avm)
- {
- if (avm == null)
- return null;
- StringBuilder sb = new StringBuilder();
- for (Arg a : avm.getArgKeys())
- {
- ArgValues v = avm.getArgValues(a);
- sb.append(v.toString());
- sb.append("\n");
- }
- return sb.toString();
- }
-
- public static SubVals getSubVals(String item)
- {
- return new SubVals(item);
- }
-
- /**
- * A helper class to keep an index of argument position with argument values
- */
- public static class ArgValue
- {
- private int argIndex;
-
- private String value;
-
- private String id;
-
- protected ArgValue(String value, int argIndex)
- {
- this.value = value;
- this.argIndex = argIndex;
- }
-
- protected String getValue()
- {
- return value;
- }
-
- protected int getArgIndex()
- {
- return argIndex;
- }
-
- protected void setId(String i)
- {
- id = i;
- }
-
- protected String getId()
- {
- return id;
- }
- }
-
- /**
- * A helper class to parse a string of the possible forms "content"
- * "[index]content", "[keyName=keyValue]content" and return the integer index,
- * the strings keyName and keyValue, and the content after the square brackets
- * (if present). Values not set `will be -1 or null.
- */
- public static class SubVals
- {
- private static int NOTSET = -1;
-
- private int index = NOTSET;
-
- private Map<String, String> subVals = null;
-
- private static char SEPARATOR = ';';
-
- private String content = null;
-
- public SubVals(String item)
- {
- this.parseVals(item);
- }
-
- public void parseVals(String item)
- {
- if (item == null)
- return;
- if (item.indexOf('[') == 0 && item.indexOf(']') > 1)
- {
- int openBracket = 0;
- int closeBracket = item.indexOf(']');
- String subvalsString = item.substring(openBracket + 1,
- closeBracket);
- this.content = item.substring(closeBracket + 1);
- boolean setIndex = false;
- for (String subvalString : subvalsString
- .split(Character.toString(SEPARATOR)))
- {
- int equals = subvalString.indexOf('=');
- if (equals > -1)
- {
- if (subVals == null)
- subVals = new HashMap<>();
- subVals.put(subvalString.substring(0, equals),
- subvalString.substring(equals + 1));
- }
- else
- {
- try
- {
- this.index = Integer.parseInt(subvalString);
- setIndex = true;
- } catch (NumberFormatException e)
- {
- Console.warn("Failed to obtain subvalue or index from '"
- + item + "'. Setting index=0 and using content='"
- + content + "'.");
- }
- }
- }
- if (!setIndex)
- this.index = NOTSET;
- }
- else
- {
- this.content = item;
- }
- }
-
- public boolean notSet()
- {
- // notSet is true if content present but nonsensical
- return index == NOTSET && subVals == null;
- }
-
- public String get(String key)
- {
- return subVals == null ? null : subVals.get(key);
- }
-
- public boolean has(String key)
- {
- return subVals == null ? false : subVals.containsKey(key);
- }
-
- public int getIndex()
- {
- return index;
- }
-
- public String getContent()
- {
- return content;
- }
- }
-
- /**
- * Helper class to allow easy extraction of information about specific
- * argument values (without having to check for null etc all the time)
- */
- protected static class ArgValuesMap
- {
- protected Map<Arg, ArgValues> m;
-
- protected ArgValuesMap()
- {
- this.newMap();
- }
-
- protected ArgValuesMap(Map<Arg, ArgValues> map)
- {
- this.m = map;
- }
-
- private Map<Arg, ArgValues> getMap()
- {
- return m;
- }
-
- private void newMap()
- {
- m = new HashMap<Arg, ArgValues>();
- }
-
- private void newArg(Arg a)
- {
- if (m == null)
- newMap();
- if (!containsArg(a))
- m.put(a, new ArgValues(a));
- }
-
- protected void addArgValue(Arg a, ArgValue av)
- {
- if (getMap() == null)
- m = new HashMap<Arg, ArgValues>();
-
- if (!m.containsKey(a))
- m.put(a, new ArgValues(a));
- ArgValues avs = m.get(a);
- avs.addArgValue(av);
- }
-
- protected ArgValues getArgValues(Arg a)
- {
- return m == null ? null : m.get(a);
- }
-
- protected ArgValues getOrCreateArgValues(Arg a)
- {
- ArgValues avs = m.get(a);
- if (avs == null)
- newArg(a);
- return getArgValues(a);
- }
-
- protected List<ArgValue> getArgValueList(Arg a)
- {
- ArgValues avs = getArgValues(a);
- return avs == null ? new ArrayList<>() : avs.getArgValueList();
- }
-
- protected ArgValue getArgValue(Arg a)
- {
- List<ArgValue> vals = getArgValueList(a);
- return (vals == null || vals.size() == 0) ? null : vals.get(0);
- }
-
- protected String getValue(Arg a)
- {
- ArgValue av = getArgValue(a);
- return av == null ? null : av.getValue();
- }
-
- protected boolean containsArg(Arg a)
- {
- if (m == null || !m.containsKey(a))
- return false;
- return a.hasOption(Opt.STRING) ? getArgValue(a) != null
- : this.getBoolean(a);
- }
-
- protected boolean hasValue(Arg a, String val)
- {
- if (m == null || !m.containsKey(a))
- return false;
- for (ArgValue av : getArgValueList(a))
- {
- String avVal = av.getValue();
- if ((val == null && avVal == null)
- || (val != null && val.equals(avVal)))
- {
- return true;
- }
- }
- return false;
- }
-
- protected boolean getBoolean(Arg a)
- {
- ArgValues av = getArgValues(a);
- return av == null ? false : av.getBoolean();
- }
-
- protected Set<Arg> getArgKeys()
- {
- return m.keySet();
- }
-
- protected ArgValue getClosestPreviousArgValueOfArg(ArgValue thisAv,
- Arg a)
- {
- ArgValue closestAv = null;
- int thisArgIndex = thisAv.getArgIndex();
- ArgValues compareAvs = this.getArgValues(a);
- int closestPreviousIndex = -1;
- for (ArgValue av : compareAvs.getArgValueList())
- {
- int argIndex = av.getArgIndex();
- if (argIndex < thisArgIndex && argIndex > closestPreviousIndex)
- {
- closestPreviousIndex = argIndex;
- closestAv = av;
- }
- }
- return closestAv;
- }
-
- protected ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a)
- {
- // this looks for the *next* arg that *might* be referring back to
- // a thisAv. Such an arg would have no subValues (if it does it should
- // specify an id in the subValues so wouldn't need to be guessed).
- ArgValue closestAv = null;
- int thisArgIndex = thisAv.getArgIndex();
- ArgValues compareAvs = this.getArgValues(a);
- int closestNextIndex = Integer.MAX_VALUE;
- for (ArgValue av : compareAvs.getArgValueList())
- {
- int argIndex = av.getArgIndex();
- if (argIndex > thisArgIndex && argIndex < closestNextIndex)
- {
- closestNextIndex = argIndex;
- closestAv = av;
- }
- }
- return closestAv;
- }
-
- protected ArgValue[] getArgValuesReferringTo(String key, String value,
- Arg a)
- {
- // this looks for the *next* arg that *might* be referring back to
- // a thisAv. Such an arg would have no subValues (if it does it should
- // specify an id in the subValues so wouldn't need to be guessed).
- List<ArgValue> avList = new ArrayList<>();
- Arg[] args = a == null ? (Arg[]) this.getMap().keySet().toArray()
- : new Arg[]
- { a };
- for (Arg keyArg : args)
- {
- for (ArgValue av : this.getArgValueList(keyArg))
- {
-
- }
- }
- return (ArgValue[]) avList.toArray();
- }
-
- protected boolean hasId(Arg a, String id)
- {
- ArgValues avs = this.getArgValues(a);
- return avs == null ? false : avs.hasId(id);
- }
-
- protected ArgValue getId(Arg a, String id)
- {
- ArgValues avs = this.getArgValues(a);
- return avs == null ? null : avs.getId(id);
- }
- }
-
- public static ArgParser parseArgFiles(List<String> argFilenameGlobs)
- {
- List<File> argFiles = new ArrayList<>();
-
- for (String pattern : argFilenameGlobs)
- {
- // I don't think we want to dedup files, making life easier
- argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
- }
-
- return parseArgFileList(argFiles);
- }
-
- public static ArgParser parseArgFileList(List<File> argFiles)
- {
- List<String> argsList = new ArrayList<>();
- for (File argFile : argFiles)
- {
- if (!argFile.exists())
- {
- String message = DOUBLEDASH
- + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\""
- + argFile.getPath() + "\": File does not exist.";
- Jalview.exit(message, 2);
- }
- try
- {
- argsList.addAll(Files.readAllLines(Paths.get(argFile.getPath())));
- } catch (IOException e)
- {
- String message = DOUBLEDASH
- + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\""
- + argFile.getPath() + "\": File could not be read.";
- Jalview.exit(message, 3);
- }
- }
- return new ArgParser(argsList);
- }
-
- public static class BootstrapArgs
- {
- // only need one
- private static Map<Arg, List<String>> bootstrapArgMap = new HashMap<>();
-
- public static BootstrapArgs getBootstrapArgs(String[] args)
- {
- List<String> argList = new ArrayList<>(Arrays.asList(args));
- return new BootstrapArgs(argList);
- }
-
- private BootstrapArgs(List<String> args)
- {
- init(args);
- }
-
- private void init(List<String> args)
- {
- if (args == null)
- return;
- for (int i = 0; i < args.size(); i++)
- {
- String arg = args.get(i);
- String argName = null;
- String val = null;
- if (arg.startsWith(ArgParser.DOUBLEDASH))
- {
- int equalPos = arg.indexOf('=');
- if (equalPos > -1)
- {
- argName = arg.substring(ArgParser.DOUBLEDASH.length(),
- equalPos);
- val = arg.substring(equalPos + 1);
- }
- else
- {
- argName = arg.substring(ArgParser.DOUBLEDASH.length());
- val = "true";
- }
-
- Arg a = argMap.get(argName);
-
- if (a == null || !a.hasOption(Opt.BOOTSTRAP))
- {
- // not a valid bootstrap arg
- continue;
- }
-
- if (a.hasOption(Opt.STRING))
- {
- if (equalPos == -1)
- {
- addAll(a, ArgParser.getShellGlobbedFilenameValues(a, args,
- i + 1));
- }
- else
- {
- if (a.hasOption(Opt.GLOB))
- addAll(a, FileUtils.getFilenamesFromGlob(val));
- else
- add(a, val);
- }
- }
- else
- {
- add(a, val);
- }
- }
- }
- }
-
- public boolean contains(Arg a)
- {
- return bootstrapArgMap.containsKey(a);
- }
-
- public List<String> getList(Arg a)
- {
- return bootstrapArgMap.get(a);
- }
-
- private List<String> getOrCreateList(Arg a)
- {
- List<String> l = getList(a);
- if (l == null)
- {
- l = new ArrayList<>();
- putList(a, l);
- }
- return l;
- }
-
- private void putList(Arg a, List<String> l)
- {
- bootstrapArgMap.put(a, l);
- }
-
- /*
- * Creates a new list if not used before,
- * adds the value unless the existing list is non-empty
- * and the arg is not MULTI (so first expressed value is
- * retained).
- */
- private void add(Arg a, String s)
- {
- List<String> l = getOrCreateList(a);
- if (a.hasOption(Opt.MULTI) || l.size() == 0)
- {
- l.add(s);
- }
- }
-
- private void addAll(Arg a, List<String> al)
- {
- List<String> l = getOrCreateList(a);
- if (a.hasOption(Opt.MULTI))
- {
- l.addAll(al);
- }
- }
-
- /*
- * Retrieves the first value even if MULTI.
- * A convenience for non-MULTI args.
- */
- public String get(Arg a)
- {
- if (!bootstrapArgMap.containsKey(a))
- return null;
- List<String> aL = bootstrapArgMap.get(a);
- return (aL == null || aL.size() == 0) ? null : aL.get(0);
- }
- }
-}
\ No newline at end of file