+++ /dev/null
-/*
- * 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
import jalview.analysis.AlignmentUtils;
import jalview.api.AlignmentViewPanel;
-import jalview.bin.ArgParser.Arg;
-import jalview.bin.ArgParser.ArgValue;
-import jalview.bin.ArgParser.ArgValuesMap;
-import jalview.bin.ArgParser.SubVals;
+import jalview.bin.argparser.Arg;
+import jalview.bin.argparser.ArgParser;
+import jalview.bin.argparser.ArgValue;
+import jalview.bin.argparser.ArgValuesMap;
+import jalview.bin.argparser.SubVals;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.PDBEntry;
//import edu.stanford.ejalbert.launching.IBrowserLaunching;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
-import jalview.bin.ArgParser.Arg;
-import jalview.bin.ArgParser.BootstrapArgs;
+import jalview.bin.argparser.Arg;
+import jalview.bin.argparser.ArgParser;
+import jalview.bin.argparser.BootstrapArgs;
import jalview.ext.so.SequenceOntology;
import jalview.gui.AlignFrame;
import jalview.gui.Desktop;
String usrPropsFile = bootstrapArgs.contains(Arg.PROPS)
? bootstrapArgs.get(Arg.PROPS)
: aparser.getValue("props");
- Cache.loadProperties(usrPropsFile);
if (usrPropsFile != null)
{
+ Cache.loadProperties(usrPropsFile);
System.out.println(
"CMD [-props " + usrPropsFile + "] executed successfully!");
}
public static void exit(String message, int exitcode)
{
- System.err.println("####### EXITING HERE!");
Console.debug("Using Jalview.exit");
if (message != null)
if (exitcode == 0)
--- /dev/null
+package jalview.bin.argparser;
+
+import java.util.Locale;
+
+public enum Arg
+{
+ 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;
+
+ protected static enum Opt
+ {
+ BOOLEAN, STRING, UNARY, MULTI, LINKED, NODUPLICATEVALUES, BOOTSTRAP,
+ GLOB, NOACTION, ALLOWSUBSTITUTIONS
+ }
+
+ 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;
+ }
+}
--- /dev/null
+/*
+ * 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.argparser;
+
+import java.io.File;
+import java.io.IOException;
+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 jalview.bin.Console;
+import jalview.bin.Jalview;
+import jalview.bin.argparser.Arg.Opt;
+import jalview.util.FileUtils;
+
+public class ArgParser
+{
+ protected static final String DOUBLEDASH = "--";
+
+ protected static final String NEGATESTRING = "no";
+
+ // the default linked id prefix used for no id (not even square braces)
+ protected 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;
+
+ protected static final Map<String, Arg> argMap;
+
+ protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
+
+ protected List<String> linkedOrder = null;
+
+ protected 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.
+ */
+ protected 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);
+ }
+
+ 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);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package jalview.bin.argparser;
+
+/**
+ * A helper class to keep an index of argument position with argument values
+ */
+public class ArgValue
+{
+ private int argIndex;
+
+ private String value;
+
+ private String id;
+
+ protected ArgValue(String value, int argIndex)
+ {
+ this.value = value;
+ this.argIndex = argIndex;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public int getArgIndex()
+ {
+ return argIndex;
+ }
+
+ protected void setId(String i)
+ {
+ id = i;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+}
\ No newline at end of file
--- /dev/null
+package jalview.bin.argparser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jalview.bin.Console;
+import jalview.bin.argparser.Arg.Opt;
+
+public class ArgValues
+{
+ protected 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+package jalview.bin.argparser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jalview.bin.argparser.Arg.Opt;
+
+/**
+ * Helper class to allow easy extraction of information about specific argument
+ * values (without having to check for null etc all the time)
+ */
+public 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);
+ }
+
+ public ArgValues getArgValues(Arg a)
+ {
+ return m == null ? null : m.get(a);
+ }
+
+ public ArgValues getOrCreateArgValues(Arg a)
+ {
+ ArgValues avs = m.get(a);
+ if (avs == null)
+ newArg(a);
+ return getArgValues(a);
+ }
+
+ public List<ArgValue> getArgValueList(Arg a)
+ {
+ ArgValues avs = getArgValues(a);
+ return avs == null ? new ArrayList<>() : avs.getArgValueList();
+ }
+
+ public ArgValue getArgValue(Arg a)
+ {
+ List<ArgValue> vals = getArgValueList(a);
+ return (vals == null || vals.size() == 0) ? null : vals.get(0);
+ }
+
+ public String getValue(Arg a)
+ {
+ ArgValue av = getArgValue(a);
+ return av == null ? null : av.getValue();
+ }
+
+ public boolean containsArg(Arg a)
+ {
+ if (m == null || !m.containsKey(a))
+ return false;
+ return a.hasOption(Opt.STRING) ? getArgValue(a) != null
+ : this.getBoolean(a);
+ }
+
+ public 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;
+ }
+
+ public boolean getBoolean(Arg a)
+ {
+ ArgValues av = getArgValues(a);
+ return av == null ? false : av.getBoolean();
+ }
+
+ public Set<Arg> getArgKeys()
+ {
+ return m.keySet();
+ }
+
+ public 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;
+ }
+
+ public 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;
+ }
+
+ public 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();
+ }
+
+ public boolean hasId(Arg a, String id)
+ {
+ ArgValues avs = this.getArgValues(a);
+ return avs == null ? false : avs.hasId(id);
+ }
+
+ public ArgValue getId(Arg a, String id)
+ {
+ ArgValues avs = this.getArgValues(a);
+ return avs == null ? null : avs.getId(id);
+ }
+}
--- /dev/null
+package jalview.bin.argparser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jalview.bin.argparser.Arg.Opt;
+import jalview.util.FileUtils;
+
+public class BootstrapArgs
+{
+ // only need one
+ private 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 = ArgParser.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);
+ }
+}
--- /dev/null
+package jalview.bin.argparser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jalview.bin.Console;
+
+/**
+ * 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 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;
+ }
+}
\ No newline at end of file
}
for (AlignFrame af : frames)
{
+ System.out.println("###### frames=" + frames);
+ System.out.println("###### af=" + af);
+ System.out.println("###### af.alignPanels=" + af.alignPanels);
for (AlignmentPanel ap : af.alignPanels)
{
if (alignmentId == null
@Override
public void run()
{
+ System.out.println("######## Starting FileLoader.run()");
+ System.out.println("######## loading file " + file);
String title = protocol == DataSourceType.PASTE
? "Copied From Clipboard"
: file;
import java.util.Properties;
import org.testng.Assert;
+import org.testng.annotations.AfterClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
-import jalview.bin.ArgParser.Arg;
-import jalview.bin.ArgParser.BootstrapArgs;
+import jalview.bin.argparser.Arg;
+import jalview.bin.argparser.ArgParser;
+import jalview.bin.argparser.BootstrapArgs;
@Test(singleThreaded = true)
public class ArgParserTest
{
+ @AfterClass(alwaysRun = true)
+ public static void resetProps()
+ {
+ Cache.applicationProperties.clear();
+ Cache.loadProperties("test/jalview/testProps.jvprops");
+ }
@Test(groups = "Functional", dataProvider = "argLines")
public void parseArgsAndSubValsTest(String commandLineArgs, Arg a,
@AfterClass(alwaysRun = true)
public static void resetProps()
{
+ Cache.applicationProperties.clear();
Cache.loadProperties("test/jalview/testProps.jvprops");
}
* along with Jalview. If not, see <http://www.gnu.org/licenses/>.
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
-package jalview.bin;
+package jalview.bin.argparser;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
+import jalview.bin.ArgsParser;
import jalview.gui.JvOptionPane;
import org.testng.annotations.BeforeClass;