import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import jalview.util.Platform;
private static enum Opt
{
- BOOLEAN, STRING, UNARY, MULTI, LINKED, ORDERED
+ BOOLEAN, STRING, UNARY, MULTI, LINKED, ORDERED, NODUPLICATEVALUES
}
public enum Arg
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;
+ NOSTRUCTURE, STRUCTURE, IMAGE, QUIT, DEBUG("d");
static
{
TEMPFAC.setOptions(Opt.STRING, Opt.LINKED);
TEMPFAC_LABEL.setOptions(Opt.STRING, Opt.LINKED);
TEMPFAC_DESC.setOptions(Opt.STRING, Opt.LINKED);
- TEMPFAC_SHADING.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);
NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED);
WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED);
IMAGE.setOptions(Opt.STRING, Opt.LINKED);
QUIT.setOptions(Opt.UNARY);
+ DEBUG.setOptions(Opt.BOOLEAN);
}
private final String[] argNames;
public static class ArgValues
{
+ private static final String ID = "id";
+
private Arg arg;
private int argCount = 0;
private boolean negated = false;
- private List<String> argsList;
+ 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.argsList = new ArrayList<String>();
+ this.argValueList = new ArrayList<ArgValue>();
this.boolValue = arg.getDefaultBoolValue();
}
return this.negated;
}
- protected void setBoolean(boolean b)
+ protected void setBoolean(boolean b, int i)
{
this.boolValue = b;
+ this.boolIndex = i;
}
protected boolean getBoolean()
@Override
public String toString()
{
- if (argsList == null)
+ if (argValueList == null)
return null;
StringBuilder sb = new StringBuilder();
sb.append(arg.toLongString());
{
sb.append("Values:");
boolean first = true;
- for (String v : argsList)
+ for (ArgValue av : argValueList)
{
+ String v = av.getValue();
if (!first)
sb.append(",");
sb.append("\n '");
protected void addValue()
{
- addValue(null);
+ addValue(null, -1);
}
- protected void addValue(String val)
+ protected void addValue(String val, int argIndex)
{
- addValue(val, false);
+ addArgValue(new ArgValue(val, argIndex));
}
- protected void addValue(String val, boolean noDuplicates)
+ protected void addArgValue(ArgValue av)
{
- if ((!arg.hasOption(Opt.MULTI) && argsList.size() > 0)
- || (noDuplicates && argsList.contains(val)))
+ if ((!arg.hasOption(Opt.MULTI) && argValueList.size() > 0)
+ || (arg.hasOption(Opt.NODUPLICATEVALUES)
+ && argValueList.contains(av.getValue())))
return;
- if (argsList == null)
+ if (argValueList == null)
+ {
+ argValueList = new ArrayList<ArgValue>();
+ }
+ SubVals sv = ArgParser.getSubVals(av.getValue());
+ if (sv.has(ID))
{
- Console.warn("** inst");
- argsList = new ArrayList<String>();
+ String id = sv.get(ID);
+ av.setId(id);
+ idMap.put(id, av);
}
- argsList.add(val);
+ argValueList.add(av);
}
protected boolean hasValue(String val)
{
- return argsList.contains(val);
+ return argValueList.contains(val);
}
- protected String getValue()
+ protected ArgValue getArgValue()
{
if (arg.hasOption(Opt.MULTI))
Console.warn("Requesting single value for multi value argument");
- return argsList.size() > 0 ? argsList.get(0) : null;
+ return argValueList.size() > 0 ? argValueList.get(0) : null;
+ }
+
+ protected List<ArgValue> getArgValueList()
+ {
+ return argValueList;
+ }
+
+ protected boolean hasId(String id)
+ {
+ return idMap.containsKey(id);
}
- protected List<String> getValues()
+ protected ArgValue getId(String id)
{
- return argsList;
+ return idMap.get(id);
}
}
public String getValue(String arg, boolean utf8decode)
{
int index = vargs.indexOf(arg);
- String dc = null, ret = null;
+ String dc = null;
+ String ret = null;
if (index != -1)
{
ret = vargs.get(index + 1).toString();
// new style
private static final Map<String, Arg> argMap;
- private Map<String, HashMap<Arg, ArgValues>> linkedArgs = new HashMap<>();
+ private Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
private List<String> linkedOrder = null;
// new style
Enumeration<String> argE = Collections.enumeration(Arrays.asList(args));
+ int argIndex = 0;
while (argE.hasMoreElements())
{
String arg = argE.nextElement();
linkedId = DEFAULTLINKEDID;
if (!linkedArgs.containsKey(linkedId))
- linkedArgs.put(linkedId, new HashMap<>());
+ linkedArgs.put(linkedId, new ArgValuesMap());
+
+ ArgValuesMap avm = linkedArgs.get(linkedId);
- Map<Arg, ArgValues> valuesMap = linkedArgs.get(linkedId);
- if (!valuesMap.containsKey(a))
- valuesMap.put(a, new ArgValues(a));
+ 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 values = valuesMap.get(a);
- if (values == null)
+ ArgValues avs = avm.getOrCreateArgValues(a);
+ if (avs == null)
{
- values = new ArgValues(a);
+ avs = new ArgValues(a);
}
// store appropriate value
if (a.hasOption(Opt.STRING))
{
- values.addValue(val);
+ avs.addValue(val, argIndex);
}
else if (a.hasOption(Opt.BOOLEAN))
{
- values.setBoolean(!negated);
- values.setNegated(negated);
+ avs.setBoolean(!negated, argIndex);
+ avs.setNegated(negated);
}
else if (a.hasOption(Opt.UNARY))
{
- values.setBoolean(true);
+ avs.setBoolean(true, argIndex);
}
- values.incrementCount();
+ avs.incrementCount();
// store in appropriate place
if (a.hasOption(Opt.LINKED))
if (!linkedOrder.contains(linkedId))
linkedOrder.add(linkedId);
}
- // store the ArgValues
- valuesMap.put(a, values);
- // store arg in the list of args
+ // store arg in the list of args used
if (argList == null)
argList = new ArrayList<>();
if (!argList.contains(a))
public boolean isSet(String linkedId, Arg a)
{
- Map<Arg, ArgValues> m = linkedArgs.get(linkedId);
- return m == null ? false : m.containsKey(a);
+ ArgValuesMap avm = linkedArgs.get(linkedId);
+ return avm == null ? false : avm.containsArg(a);
}
public boolean getBool(Arg a)
{
- if (!a.hasOption(Opt.BOOLEAN))
+ if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
{
Console.warn("Getting boolean from non boolean Arg '" + a.getName()
+ "'.");
public boolean getBool(String linkedId, Arg a)
{
- Map<Arg, ArgValues> m = linkedArgs.get(linkedId);
- if (m == null)
+ ArgValuesMap avm = linkedArgs.get(linkedId);
+ if (avm == null)
return a.getDefaultBoolValue();
- ArgValues v = m.get(a);
- return v == null ? a.getDefaultBoolValue() : v.getBoolean();
+ ArgValues avs = avm.getArgValues(a);
+ return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
}
public List<String> linkedIds()
return linkedOrder;
}
- public HashMap<Arg, ArgValues> linkedArgs(String id)
+ public ArgValuesMap linkedArgs(String id)
{
return linkedArgs.get(id);
}
{
StringBuilder sb = new StringBuilder();
sb.append("UNLINKED\n");
- sb.append(argMapToString(linkedArgs.get(null)));
+ sb.append(argValuesMapToString(linkedArgs.get(null)));
if (linkedIds() != null)
{
sb.append("LINKED\n");
if (id == null)
continue;
- Map<Arg, ArgValues> m = linkedArgs(id);
+ ArgValuesMap avm = linkedArgs(id);
sb.append("ID: '").append(id).append("'\n");
- sb.append(argMapToString(m));
+ sb.append(argValuesMapToString(avm));
}
}
return sb.toString();
}
- private static String argMapToString(Map<Arg, ArgValues> m)
+ private static String argValuesMapToString(ArgValuesMap avm)
{
- if (m == null)
+ if (avm == null)
return null;
StringBuilder sb = new StringBuilder();
- for (Arg a : m.keySet())
+ for (Arg a : avm.getArgKeys())
{
- ArgValues v = m.get(a);
+ ArgValues v = avm.getArgValues(a);
sb.append(v.toString());
sb.append("\n");
}
return sb.toString();
}
- // Helper methods with safety checks
- protected static ArgValues getArgValues(Map<Arg, ArgValues> m, Arg a)
+ public static SubVals getSubVals(String item)
{
- return m == null ? null : m.get(a);
+ return new SubVals(item);
}
- public static List<String> getValues(Map<Arg, ArgValues> m, Arg a)
+ /**
+ * A helper class to keep an index of argument position with argument values
+ */
+ public static class ArgValue
{
- ArgValues av = getArgValues(m, a);
- return av == null ? null : av.getValues();
- }
+ private int argIndex;
- public static String getValue(Map<Arg, ArgValues> m, Arg a)
- {
- List<String> vals = getValues(m, a);
- return (vals == null || vals.size() == 0) ? null : vals.get(0);
- }
+ private String value;
- public static boolean getBoolean(Map<Arg, ArgValues> m, Arg a)
- {
- ArgValues av = getArgValues(m, a);
- return av == null ? false : av.getBoolean();
- }
+ private String id;
- public static SubVal getSubVal(String item)
- {
- return new SubVal(item);
+ 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;
+ }
}
/**
* 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 SubVal
+ public static class SubVals
{
private static int NOTSET = -1;
- protected int index = NOTSET;
+ private int index = NOTSET;
- protected String keyName = null;
+ private Map<String, String> subVals = null;
- protected String keyValue = null;
+ private static char SEPARATOR = ';';
- protected String content = null;
+ private String content = null;
- public SubVal(String item)
+ public SubVals(String item)
{
- this.parseVal(item);
+ this.parseVals(item);
}
- public void parseVal(String item)
+ public void parseVals(String item)
{
if (item.indexOf('[') == 0 && item.indexOf(']') > 1)
{
int openBracket = item.indexOf('[');
int closeBracket = item.indexOf(']');
- String indexString = item.substring(openBracket + 1, closeBracket);
+ String subvalsString = item.substring(openBracket + 1,
+ closeBracket);
this.content = item.substring(closeBracket + 1);
- int equals = indexString.indexOf('=');
- if (equals > -1)
- {
- this.keyName = indexString.substring(0, equals);
- this.keyValue = indexString.substring(equals + 1);
- this.index = -1;
- }
- else
+ boolean setIndex = false;
+ for (String subvalString : subvalsString
+ .split(Character.toString(SEPARATOR)))
{
- try
+ int equals = subvalString.indexOf('=');
+ if (equals > -1)
{
- this.index = Integer.parseInt(indexString);
- } catch (NumberFormatException e)
+ if (subVals == null)
+ subVals = new HashMap<>();
+ subVals.put(subvalString.substring(0, equals),
+ subvalString.substring(equals + 1));
+ }
+ else
{
- Console.warn("Failed to obtain subvalue or index from '" + item
- + "'. Setting index=0 and using content='" + content
- + "'.");
+ 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
{
public boolean notSet()
{
// notSet is true if content present but nonsensical
- return index == NOTSET && keyName == null && keyValue == null;
+ 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 getArgValue(a) != null;
+ }
+
+ 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);
+ }
+ }
+
+ private static final Collection<Arg> bootstrapArgs = new ArrayList(
+ Arrays.asList(Arg.PROPS, Arg.DEBUG));
+
+ public static Map<Arg, String> bootstrapArgs(String[] args)
+ {
+ Map<Arg, String> bootstrapArgMap = new HashMap<>();
+ if (args == null)
+ return bootstrapArgMap;
+ Enumeration<String> argE = Collections.enumeration(Arrays.asList(args));
+ while (argE.hasMoreElements())
+ {
+ String arg = argE.nextElement();
+ String argName = null;
+ String val = null;
+ if (arg.startsWith("--"))
+ {
+ int equalPos = arg.indexOf('=');
+ if (equalPos > -1)
+ {
+ argName = arg.substring(2, equalPos);
+ val = arg.substring(equalPos + 1);
+ }
+ else
+ {
+ argName = arg.substring(2);
+ }
+ Arg a = argMap.get(argName);
+ if (a != null && bootstrapArgs.contains(a))
+ bootstrapArgMap.put(a, val);
+ }
}
+ return bootstrapArgMap;
}
}
\ No newline at end of file