import java.util.Locale;
import java.util.Map;
+import jalview.bin.Cache;
import jalview.bin.Console;
import jalview.bin.Jalview;
import jalview.bin.argparser.Arg.Opt;
import jalview.util.FileUtils;
+import jalview.util.HttpUtils;
public class ArgParser
{
protected static final String DOUBLEDASH = "--";
+ protected static final char EQUALS = '=';
+
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 linkedId string used to match all linkedIds seen so far
+ protected static final String MATCHALLLINKEDIDS = "*";
+
// the counter added to the default linked id prefix
private int defaultLinkedIdCounter = 0;
- // the linked id prefix used for --opennew files
- protected static final String OPENNEWLINKEDIDPREFIX = "OPENNEW:";
+ // the substitution string used to use the defaultLinkedIdCounter
+ private static final String DEFAULTLINKEDIDCOUNTER = "{}";
- // the counter added to the default linked id prefix
- private int opennewLinkedIdCounter = 0;
+ // the counter added to the default linked id prefix. NOW using
+ // linkedIdAutoCounter
+ // private int openLinkedIdCounter = 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}";
+ // the linked id prefix used for --open files. NOW the same as DEFAULT
+ protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
+ // the counter used for {n} substitutions
private int linkedIdAutoCounter = 0;
+ // the linked id substitution string used to increment the idCounter (and use
+ // the incremented value)
+ private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
+
+ // the linked id substitution string used to use the idCounter
+ private static final String LINKEDIDAUTOCOUNTER = "{n}";
+
+ // the linked id substitution string used to use the base filename of --append
+ // or --open
+ private static final String LINKEDIDBASENAME = "{basename}";
+
+ // the linked id substitution string used to use the dir path of --append
+ // or --open
+ private static final String LINKEDIDDIRNAME = "{dirname}";
+
+ // the current argfile
+ private String argFile = null;
+
+ // the linked id substitution string used to use the dir path of the latest
+ // --argfile name
+ private static final String ARGFILEBASENAME = "{argfilebasename}";
+
+ // the linked id substitution string used to use the dir path of the latest
+ // --argfile name
+ private static final String ARGFILEDIRNAME = "{argfiledirname}";
+
// flag to say whether {n} subtitutions in output filenames should be made.
// Turn on and off with --subs and --nosubs
private boolean substitutions = false;
+ // flag to say whether the default linkedId is the current default linked id
+ // or ALL linkedIds
+ private boolean allLinkedIds = false;
+
protected static final Map<String, Arg> argMap;
protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
protected List<Arg> argList;
+ private static final char ARGFILECOMMENT = '#';
+
static
{
argMap = new HashMap<>();
if (argMap.containsKey(argName))
{
Console.warn("Trying to add argument name multiple times: '"
- + argName + "'"); // RESTORE THIS WHEN MERGED
+ + argName + "'"); // RESTORE THIS WHEN
+ // MERGED
if (argMap.get(argName) != a)
{
Console.error(
public ArgParser(String[] args)
{
+ this(args, false);
+ }
+
+ public ArgParser(String[] args, boolean initsubstitutions)
+ {
// Make a mutable new ArrayList so that shell globbing parser works.
// (When shell file globbing is used, there are a sequence of non-Arg
// arguments (which are the expanded globbed filenames) that need to be
- // consumed by the --open/--argfile/etc Arg which is most easily done by
+ // consumed by the --append/--argfile/etc Arg which is most easily done by
// removing these filenames from the list one at a time. This can't be done
// with an ArrayList made with only Arrays.asList(String[] args). )
- this(new ArrayList<>(Arrays.asList(args)));
+ this(new ArrayList<>(Arrays.asList(args)), initsubstitutions);
+ }
+
+ public ArgParser(List<String> args, boolean initsubstitutions)
+ {
+ this(args, initsubstitutions, false);
}
- public ArgParser(List<String> args)
+ public ArgParser(List<String> args, boolean initsubstitutions,
+ boolean allowPrivate)
{
- // do nothing if there are no "--" args and some "-" args
+ // do nothing if there are no "--" args and (some "-" args || >0 arg is
+ // "open")
boolean d = false;
boolean dd = false;
for (String arg : args)
dd = true;
break;
}
- else if (arg.startsWith("-"))
+ else if (arg.startsWith("-") || arg.equals("open"))
{
d = true;
}
if (d && !dd)
{
// leave it to the old style -- parse an empty list
- parse(new ArrayList<String>());
+ parse(new ArrayList<String>(), false, false);
return;
}
- parse(args);
+ parse(args, initsubstitutions, allowPrivate);
}
- private void parse(List<String> args)
+ private void parse(List<String> args, boolean initsubstitutions,
+ boolean allowPrivate)
{
+ this.substitutions = initsubstitutions;
int argIndex = 0;
boolean openEachInitialFilenames = true;
for (int i = 0; i < args.size(); i++)
// single arg[i] and increment the defaultLinkedIdCounter so that each of
// these files is opened separately.
if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
- && !arg.startsWith("-") && new File(arg).exists())
+ && !arg.startsWith("-") && (new File(arg).exists()
+ || HttpUtils.startsWithHttpOrHttps(arg)))
{
- arg = DOUBLEDASH + Arg.OPENNEW.getName();
+ arg = Arg.OPEN.argString();
}
else
{
String linkedId = null;
if (arg.startsWith(DOUBLEDASH))
{
- int equalPos = arg.indexOf('=');
+ int equalPos = arg.indexOf(EQUALS);
if (equalPos > -1)
{
argName = arg.substring(DOUBLEDASH.length(), equalPos);
Console.error("Argument '" + arg + "' not recognised. Ignoring.");
continue;
}
+ if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
+ {
+ Console.error(
+ "Argument '" + a.argString() + "' is private. Ignoring.");
+ continue;
+ }
if (!a.hasOption(Opt.BOOLEAN) && negated)
{
// used "no" with a non-boolean option
- Console.error("Argument '--" + NEGATESTRING + argName
+ Console.error("Argument '" + DOUBLEDASH + 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
+ Console.error("Argument '" + a.argString()
+ "' 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
+ Console.error("Argument '" + a.argString()
+ "' does not expect a linked id (given as '" + arg
+ "'). Ignoring.");
continue;
if (a.hasOption(Opt.GLOB))
{
// strip off and save the SubVals to be added individually later
- globSubVals = ArgParser.getSubVals(val);
- globVals = FileUtils
- .getFilenamesFromGlob(globSubVals.getContent());
+ globSubVals = new SubVals(val);
+ // make substitutions before looking for files
+ String fileGlob = makeSubstitutions(globSubVals.getContent(),
+ linkedId);
+ globVals = FileUtils.getFilenamesFromGlob(fileGlob);
}
else
{
{
// There is no "=" so value is next arg or args (possibly shell
// glob-expanded)
- if (i + 1 >= args.size())
+ if ((openEachInitialFilenames ? i : i + 1) >= args.size())
{
// no value to take for arg, which wants a value
Console.error("Argument '" + a.getName()
// make NOACTION adjustments
// default and auto counter increments
- if (a == Arg.INCREMENT)
+ if (a == Arg.NEWFRAME)
{
defaultLinkedIdCounter++;
}
{
substitutions = !negated;
}
+ else if (a == Arg.SETARGFILE)
+ {
+ argFile = val;
+ }
+ else if (a == Arg.UNSETARGFILE)
+ {
+ argFile = null;
+ }
+ else if (a == Arg.ALLFRAMES)
+ {
+ allLinkedIds = !negated;
+ }
String autoCounterString = null;
boolean usingAutoCounterLinkedId = false;
{
if (linkedId == null)
{
- if (a == Arg.OPENNEW)
+ if (a == Arg.OPEN)
{
- // use the next default prefixed OPENNEWLINKEDID
- linkedId = new StringBuilder(OPENNEWLINKEDIDPREFIX)
- .append(Integer.toString(opennewLinkedIdCounter))
+ // use the next default prefixed OPENLINKEDID
+ // NOW using the linkedIdAutoCounter
+ defaultLinkedIdCounter++;
+ linkedId = new StringBuilder(OPENLINKEDIDPREFIX)
+ .append(Integer.toString(defaultLinkedIdCounter))
.toString();
- opennewLinkedIdCounter++;
}
else
{
- // use default linkedId for linked arguments
- linkedId = defaultLinkedId;
- usingDefaultLinkedId = true;
- Console.debug("Changing linkedId to '" + linkedId + "' from "
- + arg);
+ if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
+ {
+ linkedId = this.MATCHALLLINKEDIDS;
+ }
+ else
+ {
+ // use default linkedId for linked arguments
+ linkedId = defaultLinkedId;
+ usingDefaultLinkedId = true;
+ Console.debug("Changing linkedId to '" + linkedId
+ + "' from " + arg);
+ }
}
}
- else if (linkedId.contains(AUTOCOUNTERLINKEDID))
+ else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
{
// turn {n} to the autoCounter
autoCounterString = Integer.toString(linkedIdAutoCounter);
- linkedId = linkedId.replace(AUTOCOUNTERLINKEDID,
+ linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
autoCounterString);
usingAutoCounterLinkedId = true;
Console.debug(
"Changing linkedId to '" + linkedId + "' from " + arg);
}
- else if (linkedId.contains(INCREMENTAUTOCOUNTERLINKEDID))
+ else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
{
// turn {++n} to the incremented autoCounter
autoCounterString = Integer.toString(++linkedIdAutoCounter);
- linkedId = linkedId.replace(INCREMENTAUTOCOUNTERLINKEDID,
+ linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
autoCounterString);
usingAutoCounterLinkedId = true;
Console.debug(
// not dealing with both NODUPLICATEVALUES and GLOB
if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
{
- Console.error("Argument '--" + argName
+ Console.error("Argument '" + a.argString()
+ "' cannot contain a duplicate value ('" + val
+ "'). Ignoring this and subsequent occurrences.");
continue;
}
// check for unique id
- SubVals idsv = ArgParser.getSubVals(val);
+ SubVals idsv = new SubVals(val);
String id = idsv.get(ArgValues.ID);
if (id != null && avm.hasId(a, id))
{
- Console.error("Argument '--" + argName + "' has a duplicate id ('"
- + id + "'). Ignoring.");
+ Console.error("Argument '" + a.argString()
+ + "' has a duplicate id ('" + id + "'). Ignoring.");
continue;
}
boolean argIndexIncremented = false;
+ /* TODO
+ * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
+ * DONE, need to check
+ */
ArgValues avs = avm.getOrCreateArgValues(a);
// store appropriate String value(s)
{
for (String v : globVals)
{
- v = makeSubstitutions(v);
SubVals vsv = new SubVals(globSubVals, v);
- avs.addValue(vsv, v, argIndex++);
+ addValue(linkedId, avs, vsv, v, argIndex++, true);
argIndexIncremented = true;
}
}
else
{
- avs.addValue(makeSubstitutions(val), argIndex);
+ addValue(linkedId, avs, val, argIndex, true);
}
}
else if (a.hasOption(Opt.BOOLEAN))
{
- avs.setBoolean(!negated, argIndex);
- avs.setNegated(negated);
+ setBoolean(linkedId, avs, !negated, argIndex);
+ setNegated(linkedId, avs, negated);
}
else if (a.hasOption(Opt.UNARY))
{
- avs.setBoolean(true, argIndex);
+ setBoolean(linkedId, avs, true, argIndex);
}
- avs.incrementCount();
+ incrementCount(linkedId, avs);
if (!argIndexIncremented)
argIndex++;
}
}
- private String makeSubstitutions(String val)
+ public String makeSubstitutions(String val, String linkedId)
{
- if (!this.substitutions)
+ if (!this.substitutions || val == null)
return val;
String subvals;
subvals = "";
rest = val;
}
- if ((rest.contains(AUTOCOUNTERLINKEDID)))
- rest = rest.replace(AUTOCOUNTERLINKEDID,
+ if (rest.contains(LINKEDIDAUTOCOUNTER))
+ rest = rest.replace(LINKEDIDAUTOCOUNTER,
String.valueOf(linkedIdAutoCounter));
- if ((rest.contains(INCREMENTAUTOCOUNTERLINKEDID)))
- rest = rest.replace(INCREMENTAUTOCOUNTERLINKEDID,
+ if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
+ rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
String.valueOf(++linkedIdAutoCounter));
- if ((rest.contains("{}")))
- rest = rest.replace("{}", String.valueOf(defaultLinkedIdCounter));
+ if (rest.contains(DEFAULTLINKEDIDCOUNTER))
+ rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
+ String.valueOf(defaultLinkedIdCounter));
+ ArgValuesMap avm = linkedArgs.get(linkedId);
+ if (avm != null)
+ {
+ if (rest.contains(LINKEDIDBASENAME))
+ {
+ rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
+ }
+ if (rest.contains(LINKEDIDDIRNAME))
+ {
+ rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
+ }
+ }
+ if (argFile != null)
+ {
+ if (rest.contains(ARGFILEBASENAME))
+ {
+ rest = rest.replace(ARGFILEBASENAME,
+ FileUtils.getBasename(new File(argFile)));
+ }
+ if (rest.contains(ARGFILEDIRNAME))
+ {
+ rest = rest.replace(ARGFILEDIRNAME,
+ FileUtils.getDirname(new File(argFile)));
+ }
+ }
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.
+ * 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)
return sb.toString();
}
- public static SubVals getSubVals(String item)
- {
- return new SubVals(item);
- }
-
- public static ArgParser parseArgFiles(List<String> argFilenameGlobs)
+ public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
+ boolean initsubstitutions)
{
List<File> argFiles = new ArrayList<>();
argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
}
- return parseArgFileList(argFiles);
+ return parseArgFileList(argFiles, initsubstitutions);
}
- public static ArgParser parseArgFileList(List<File> argFiles)
+ public static ArgParser parseArgFileList(List<File> argFiles,
+ boolean initsubstitutions)
{
List<String> argsList = new ArrayList<>();
for (File argFile : argFiles)
{
if (!argFile.exists())
{
- String message = DOUBLEDASH
- + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\""
+ String message = Arg.ARGFILE.argString() + EQUALS + "\""
+ argFile.getPath() + "\": File does not exist.";
Jalview.exit(message, 2);
}
try
{
- argsList.addAll(Files.readAllLines(Paths.get(argFile.getPath())));
+ String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
+ .append(EQUALS).append(argFile.getCanonicalPath())
+ .toString();
+ argsList.add(setargfile);
+ argsList.addAll(readArgFile(argFile));
+ argsList.add(Arg.UNSETARGFILE.argString());
} catch (IOException e)
{
- String message = DOUBLEDASH
- + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\""
- + argFile.getPath() + "\": File could not be read.";
+ String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
+ + "\": File could not be read.";
Jalview.exit(message, 3);
}
}
- return new ArgParser(argsList);
+ // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
+ // --unsetargfile
+ return new ArgParser(argsList, initsubstitutions, true);
+ }
+
+ protected static List<String> readArgFile(File argFile)
+ {
+ List<String> args = new ArrayList<>();
+ if (argFile != null && argFile.exists())
+ {
+ try
+ {
+ for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
+ {
+ if (line != null && line.length() > 0
+ && line.charAt(0) != ARGFILECOMMENT)
+ args.add(line);
+ }
+ } catch (IOException e)
+ {
+ String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
+ + "\": File could not be read.";
+ Console.debug(message, e);
+ Jalview.exit(message, 3);
+ }
+ }
+ return args;
+ }
+
+ public static enum Position
+ {
+ FIRST, BEFORE, AFTER
+ }
+
+ public static String getValueFromSubValOrArg(ArgValuesMap avm, Arg a,
+ SubVals sv)
+ {
+ return getFromSubValArgOrPref(avm, a, sv, null, null, null);
+ }
+
+ public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
+ SubVals sv, String key, String pref, String def)
+ {
+ return getFromSubValArgOrPref(avm, a, Position.FIRST, null, sv, key,
+ pref, def);
+ }
+
+ public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
+ Position pos, ArgValue av, SubVals sv, String key, String pref,
+ String def)
+ {
+ if (key == null)
+ key = a.getName();
+ if (sv != null && sv.has(key) && sv.get(key) != null)
+ return sv.get(key);
+ if (avm != null && avm.containsArg(a))
+ {
+ String val = null;
+ if (pos == Position.FIRST && avm.getValue(a) != null)
+ return avm.getValue(a);
+ else if (pos == Position.BEFORE
+ && avm.getClosestPreviousArgValueOfArg(av, a) != null)
+ return avm.getClosestPreviousArgValueOfArg(av, a).getValue();
+ else if (pos == Position.AFTER
+ && avm.getClosestNextArgValueOfArg(av, a) != null)
+ return avm.getClosestNextArgValueOfArg(av, a).getValue();
+ }
+ return pref != null ? Cache.getDefault(pref, def) : def;
+ }
+
+ public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
+ SubVals sv)
+ {
+ return getFromSubValArgOrPref(avm, a, sv, null, null, false);
+ }
+
+ public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
+ SubVals sv, String key, String pref, boolean def)
+ {
+ if (key == null)
+ key = a.getName();
+ if (sv != null && sv.has(key) && sv.get(key) != null)
+ return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
+ if (avm != null && avm.containsArg(a))
+ return avm.getBoolean(a);
+ return pref != null ? Cache.getDefault(pref, def) : def;
+ }
+
+ // the following methods look for the "*" linkedId and add the argvalue to all
+ // linkedId ArgValues if it does
+ private void addValue(String linkedId, ArgValues avs, SubVals sv,
+ String v, int argIndex, boolean doSubs)
+ {
+ Arg a = avs.arg();
+ if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
+ {
+ for (String id : linkedOrder)
+ {
+ ArgValuesMap avm = linkedArgs.get(id);
+ ArgValues tavs = avm.getArgValues(a);
+ String val = v;
+ if (doSubs)
+ {
+ val = makeSubstitutions(v, linkedId);
+ sv = new SubVals(sv, val);
+ }
+ tavs.addValue(sv, val, argIndex);
+ }
+ }
+ else
+ {
+ String val = v;
+ if (doSubs)
+ {
+ val = makeSubstitutions(v, linkedId);
+ sv = new SubVals(sv, val);
+ }
+ avs.addValue(sv, val, argIndex);
+ }
+ }
+
+ private void addValue(String linkedId, ArgValues avs, String v,
+ int argIndex, boolean doSubs)
+ {
+ Arg a = avs.arg();
+ if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
+ {
+ for (String id : linkedOrder)
+ {
+ ArgValuesMap avm = linkedArgs.get(id);
+ ArgValues tavs = avm.getArgValues(a);
+ String val = doSubs ? makeSubstitutions(v, linkedId) : v;
+ tavs.addValue(val, argIndex);
+ }
+ }
+ else
+ {
+ String val = doSubs ? makeSubstitutions(v, linkedId) : v;
+ avs.addValue(val, argIndex);
+ }
+ }
+
+ private void setBoolean(String linkedId, ArgValues avs, boolean b,
+ int argIndex)
+ {
+ Arg a = avs.arg();
+ if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
+ {
+ for (String id : linkedOrder)
+ {
+ ArgValuesMap avm = linkedArgs.get(id);
+ ArgValues tavs = avm.getArgValues(a);
+ tavs.setBoolean(b, argIndex);
+ }
+ }
+ else
+ {
+ avs.setBoolean(b, argIndex);
+ }
+ }
+
+ private void setNegated(String linkedId, ArgValues avs, boolean b)
+ {
+ Arg a = avs.arg();
+ if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
+ {
+ for (String id : linkedOrder)
+ {
+ ArgValuesMap avm = linkedArgs.get(id);
+ ArgValues tavs = avm.getArgValues(a);
+ tavs.setNegated(b);
+ }
+ }
+ else
+ {
+ avs.setNegated(b);
+ }
+ }
+
+ private void incrementCount(String linkedId, ArgValues avs)
+ {
+ Arg a = avs.arg();
+ if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
+ {
+ for (String id : linkedOrder)
+ {
+ ArgValuesMap avm = linkedArgs.get(id);
+ ArgValues tavs = avm.getArgValues(a);
+ tavs.incrementCount();
+ }
+ }
+ else
+ {
+ avs.incrementCount();
+ }
}
}
\ No newline at end of file