From 2e59bc7938c0f10bde61fd30d1841bedb3d8319e Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Thu, 23 Mar 2023 01:11:40 +0000 Subject: [PATCH] JAL-629 refactor ArgParser and helper classes all to jalview.bin.argparser to remove static classes. Clear properties after tests. --- src/jalview/bin/ArgParser.java | 1354 -------------------- src/jalview/bin/Commands.java | 9 +- src/jalview/bin/Jalview.java | 8 +- src/jalview/bin/argparser/Arg.java | 180 +++ src/jalview/bin/argparser/ArgParser.java | 514 ++++++++ src/jalview/bin/argparser/ArgValue.java | 39 + src/jalview/bin/argparser/ArgValues.java | 160 +++ src/jalview/bin/argparser/ArgValuesMap.java | 193 +++ src/jalview/bin/argparser/BootstrapArgs.java | 143 +++ src/jalview/bin/argparser/SubVals.java | 101 ++ src/jalview/gui/Desktop.java | 3 + src/jalview/io/FileLoader.java | 2 + test/jalview/bin/ArgParserTest.java | 12 +- test/jalview/bin/CommandsTest.java | 1 + .../bin/{ => argparser}/ArgsParserTest.java | 3 +- 15 files changed, 1357 insertions(+), 1365 deletions(-) delete mode 100644 src/jalview/bin/ArgParser.java create mode 100644 src/jalview/bin/argparser/Arg.java create mode 100644 src/jalview/bin/argparser/ArgParser.java create mode 100644 src/jalview/bin/argparser/ArgValue.java create mode 100644 src/jalview/bin/argparser/ArgValues.java create mode 100644 src/jalview/bin/argparser/ArgValuesMap.java create mode 100644 src/jalview/bin/argparser/BootstrapArgs.java create mode 100644 src/jalview/bin/argparser/SubVals.java rename test/jalview/bin/{ => argparser}/ArgsParserTest.java (98%) diff --git a/src/jalview/bin/ArgParser.java b/src/jalview/bin/ArgParser.java deleted file mode 100644 index fca2320..0000000 --- a/src/jalview/bin/ArgParser.java +++ /dev/null @@ -1,1354 +0,0 @@ -/* - * 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 . - * 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 argsIndexes; - - private List argValueList; - - private Map idMap = new HashMap<>(); - - protected ArgValues(Arg a) - { - this.arg = a; - this.argValueList = new ArrayList(); - 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(); - } - 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 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 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 argMap; - - private Map linkedArgs = new HashMap<>(); - - private List linkedOrder = null; - - private List 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 args) - { - init(args); - } - - private void init(List args) - { - // Enumeration 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 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 {"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 getShellGlobbedFilenameValues(Arg a, - List args, int i) - { - List 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 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 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 m; - - protected ArgValuesMap() - { - this.newMap(); - } - - protected ArgValuesMap(Map map) - { - this.m = map; - } - - private Map getMap() - { - return m; - } - - private void newMap() - { - m = new HashMap(); - } - - 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(); - - 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 getArgValueList(Arg a) - { - ArgValues avs = getArgValues(a); - return avs == null ? new ArrayList<>() : avs.getArgValueList(); - } - - protected ArgValue getArgValue(Arg a) - { - List 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 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 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 argFilenameGlobs) - { - List 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 argFiles) - { - List 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> bootstrapArgMap = new HashMap<>(); - - public static BootstrapArgs getBootstrapArgs(String[] args) - { - List argList = new ArrayList<>(Arrays.asList(args)); - return new BootstrapArgs(argList); - } - - private BootstrapArgs(List args) - { - init(args); - } - - private void init(List 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 getList(Arg a) - { - return bootstrapArgMap.get(a); - } - - private List getOrCreateList(Arg a) - { - List l = getList(a); - if (l == null) - { - l = new ArrayList<>(); - putList(a, l); - } - return l; - } - - private void putList(Arg a, List 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 l = getOrCreateList(a); - if (a.hasOption(Opt.MULTI) || l.size() == 0) - { - l.add(s); - } - } - - private void addAll(Arg a, List al) - { - List 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 aL = bootstrapArgMap.get(a); - return (aL == null || aL.size() == 0) ? null : aL.get(0); - } - } -} \ No newline at end of file diff --git a/src/jalview/bin/Commands.java b/src/jalview/bin/Commands.java index f71ebf3..24a3c22 100644 --- a/src/jalview/bin/Commands.java +++ b/src/jalview/bin/Commands.java @@ -13,10 +13,11 @@ import java.util.Map; 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; diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 046ad90..9e488dc 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -64,8 +64,9 @@ import com.threerings.getdown.util.LaunchUtil; //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; @@ -415,9 +416,9 @@ public class Jalview 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!"); } @@ -1599,7 +1600,6 @@ public class Jalview public static void exit(String message, int exitcode) { - System.err.println("####### EXITING HERE!"); Console.debug("Using Jalview.exit"); if (message != null) if (exitcode == 0) diff --git a/src/jalview/bin/argparser/Arg.java b/src/jalview/bin/argparser/Arg.java new file mode 100644 index 0000000..092f5d2 --- /dev/null +++ b/src/jalview/bin/argparser/Arg.java @@ -0,0 +1,180 @@ +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; + } +} diff --git a/src/jalview/bin/argparser/ArgParser.java b/src/jalview/bin/argparser/ArgParser.java new file mode 100644 index 0000000..82b1b92 --- /dev/null +++ b/src/jalview/bin/argparser/ArgParser.java @@ -0,0 +1,514 @@ +/* + * 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 . + * 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 argMap; + + protected Map linkedArgs = new HashMap<>(); + + protected List linkedOrder = null; + + protected List 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 args) + { + init(args); + } + + private void init(List args) + { + // Enumeration 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 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 {"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 getShellGlobbedFilenameValues(Arg a, + List args, int i) + { + List 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 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 argFilenameGlobs) + { + List 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 argFiles) + { + List 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 diff --git a/src/jalview/bin/argparser/ArgValue.java b/src/jalview/bin/argparser/ArgValue.java new file mode 100644 index 0000000..be6227d --- /dev/null +++ b/src/jalview/bin/argparser/ArgValue.java @@ -0,0 +1,39 @@ +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 diff --git a/src/jalview/bin/argparser/ArgValues.java b/src/jalview/bin/argparser/ArgValues.java new file mode 100644 index 0000000..0be7768 --- /dev/null +++ b/src/jalview/bin/argparser/ArgValues.java @@ -0,0 +1,160 @@ +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 argsIndexes; + + private List argValueList; + + private Map idMap = new HashMap<>(); + + protected ArgValues(Arg a) + { + this.arg = a; + this.argValueList = new ArrayList(); + 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(); + } + 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 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 diff --git a/src/jalview/bin/argparser/ArgValuesMap.java b/src/jalview/bin/argparser/ArgValuesMap.java new file mode 100644 index 0000000..22d85a8 --- /dev/null +++ b/src/jalview/bin/argparser/ArgValuesMap.java @@ -0,0 +1,193 @@ +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 m; + + protected ArgValuesMap() + { + this.newMap(); + } + + protected ArgValuesMap(Map map) + { + this.m = map; + } + + private Map getMap() + { + return m; + } + + private void newMap() + { + m = new HashMap(); + } + + 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(); + + 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 getArgValueList(Arg a) + { + ArgValues avs = getArgValues(a); + return avs == null ? new ArrayList<>() : avs.getArgValueList(); + } + + public ArgValue getArgValue(Arg a) + { + List 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 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 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); + } +} diff --git a/src/jalview/bin/argparser/BootstrapArgs.java b/src/jalview/bin/argparser/BootstrapArgs.java new file mode 100644 index 0000000..bcb86f9 --- /dev/null +++ b/src/jalview/bin/argparser/BootstrapArgs.java @@ -0,0 +1,143 @@ +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> bootstrapArgMap = new HashMap<>(); + + public static BootstrapArgs getBootstrapArgs(String[] args) + { + List argList = new ArrayList<>(Arrays.asList(args)); + return new BootstrapArgs(argList); + } + + private BootstrapArgs(List args) + { + init(args); + } + + private void init(List 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 getList(Arg a) + { + return bootstrapArgMap.get(a); + } + + private List getOrCreateList(Arg a) + { + List l = getList(a); + if (l == null) + { + l = new ArrayList<>(); + putList(a, l); + } + return l; + } + + private void putList(Arg a, List 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 l = getOrCreateList(a); + if (a.hasOption(Opt.MULTI) || l.size() == 0) + { + l.add(s); + } + } + + private void addAll(Arg a, List al) + { + List 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 aL = bootstrapArgMap.get(a); + return (aL == null || aL.size() == 0) ? null : aL.get(0); + } +} diff --git a/src/jalview/bin/argparser/SubVals.java b/src/jalview/bin/argparser/SubVals.java new file mode 100644 index 0000000..39b48d3 --- /dev/null +++ b/src/jalview/bin/argparser/SubVals.java @@ -0,0 +1,101 @@ +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 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 diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 981a060..20543af 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -2033,6 +2033,9 @@ public class Desktop extends jalview.jbgui.GDesktop } 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 diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 971aba3..3b4cc31 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -288,6 +288,8 @@ public class FileLoader implements Runnable @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; diff --git a/test/jalview/bin/ArgParserTest.java b/test/jalview/bin/ArgParserTest.java index b72fcb9..4487178 100644 --- a/test/jalview/bin/ArgParserTest.java +++ b/test/jalview/bin/ArgParserTest.java @@ -6,15 +6,23 @@ import java.util.List; 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, diff --git a/test/jalview/bin/CommandsTest.java b/test/jalview/bin/CommandsTest.java index c5f8bff..2be8458 100644 --- a/test/jalview/bin/CommandsTest.java +++ b/test/jalview/bin/CommandsTest.java @@ -28,6 +28,7 @@ public class CommandsTest @AfterClass(alwaysRun = true) public static void resetProps() { + Cache.applicationProperties.clear(); Cache.loadProperties("test/jalview/testProps.jvprops"); } diff --git a/test/jalview/bin/ArgsParserTest.java b/test/jalview/bin/argparser/ArgsParserTest.java similarity index 98% rename from test/jalview/bin/ArgsParserTest.java rename to test/jalview/bin/argparser/ArgsParserTest.java index 23db36f..a163a49 100644 --- a/test/jalview/bin/ArgsParserTest.java +++ b/test/jalview/bin/argparser/ArgsParserTest.java @@ -18,13 +18,14 @@ * along with Jalview. If not, see . * 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; -- 1.7.10.2