X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fbin%2FArgParser.java;h=77b3e8718694d052ad0de1349fff9f75a16d51b3;hb=72479cdc25ff4d23a23b38e5bb25e01cd18bcd45;hp=3b08013d13e633c7057ce19f606351538d898f6a;hpb=5afaa80bd2cddc6651867f86a04affffa0896f89;p=jalview.git diff --git a/src/jalview/bin/ArgParser.java b/src/jalview/bin/ArgParser.java index 3b08013..77b3e87 100644 --- a/src/jalview/bin/ArgParser.java +++ b/src/jalview/bin/ArgParser.java @@ -27,9 +27,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.EnumSet; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -40,22 +38,28 @@ import jalview.util.FileUtils; public class ArgParser { + private static final String DOUBLEDASH = "--"; + private static final String NEGATESTRING = "no"; + // the linked id used for no id (not even square braces) private static final String DEFAULTLINKEDID = ""; + // 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; + private static enum Opt { BOOLEAN, STRING, UNARY, MULTI, LINKED, NODUPLICATEVALUES, BOOTSTRAP, GLOB } - // These bootstrap args are simply parsed before a full parse of arguments and - // so are accessible at an earlier stage to (e.g.) set debug log leve, provide - // a props file (that might set log level), run headlessly, read an argfile - // instead of other args. - private static final List bootstrapArgs; - public enum Arg { /* @@ -98,7 +102,7 @@ public class ArgParser // expects a string value SORTBYTREE.setOptions(true, Opt.BOOLEAN); USAGESTATS.setOptions(true, Opt.BOOLEAN); - OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI); + OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB); OPEN2.setOptions(Opt.STRING, Opt.LINKED); PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP); QUESTIONNAIRE.setOptions(Opt.STRING); @@ -128,6 +132,11 @@ public class ArgParser DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP); QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP); ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB); + // 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; @@ -423,16 +432,6 @@ public class ArgParser return ret; } - /* - public Object getAppletValue(String key, String def, boolean asString) - { - Object value; - return (appletParams == null ? null - : (value = appletParams.get(key.toLowerCase())) == null ? def - : asString ? "" + value : value); - } - */ - // new style private static final Map argMap; @@ -445,11 +444,8 @@ public class ArgParser static { argMap = new HashMap<>(); - bootstrapArgs = new ArrayList<>(); for (Arg a : EnumSet.allOf(Arg.class)) { - if (a.hasOption(Opt.BOOTSTRAP)) - bootstrapArgs.add(a); for (String argName : a.getNames()) { if (argMap.containsKey(argName)) @@ -473,30 +469,39 @@ public class ArgParser public ArgParser(String[] args) { - this(Arrays.asList(args)); + // make a mutable new ArrayList so that shell globbing parser works + this(new ArrayList<>(Arrays.asList(args))); } public ArgParser(List args) { - Enumeration argE = Collections.enumeration(args); + init(args); + } + + private void init(List args) + { + // Enumeration argE = Collections.enumeration(args); int argIndex = 0; - while (argE.hasMoreElements()) + // while (argE.hasMoreElements()) + for (int i = 0; i < args.size(); i++) { - String arg = argE.nextElement(); + // 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("--")) + if (arg.startsWith(DOUBLEDASH)) { int equalPos = arg.indexOf('='); if (equalPos > -1) { - argName = arg.substring(2, equalPos); + argName = arg.substring(DOUBLEDASH.length(), equalPos); val = arg.substring(equalPos + 1); } else { - argName = arg.substring(2); + argName = arg.substring(DOUBLEDASH.length()); } int idOpen = argName.indexOf('['); int idClose = argName.indexOf(']'); @@ -552,25 +557,62 @@ public class ArgParser if (a.hasOption(Opt.STRING) && equalPos == -1) { // take next arg as value if required, and '=' was not found - if (!argE.hasMoreElements()) + // 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; } - val = argE.nextElement(); + // 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); + } } - // use default linkedId for linked arguments - if (a.hasOption(Opt.LINKED) && linkedId == null) - linkedId = DEFAULTLINKEDID; + String autoCounterString = null; + if (a.hasOption(Opt.LINKED)) + { + if (linkedId == null) + { + // use default linkedId for linked arguments + linkedId = DEFAULTLINKEDID; + Console.debug( + "Changing linkedId to '" + linkedId + "' from " + arg); + } + else if (linkedId.equals(AUTOCOUNTERLINKEDID)) + { + // turn {n} to the autoCounter + autoCounterString = Integer.toString(idCounter); + linkedId = autoCounterString; + 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; + Console.debug( + "Changing linkedId to '" + linkedId + "' from " + arg); + } + } if (!linkedArgs.containsKey(linkedId)) linkedArgs.put(linkedId, new ArgValuesMap()); 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 @@ -597,7 +639,15 @@ public class ArgParser // store appropriate value if (a.hasOption(Opt.STRING)) { - avs.addValue(val, argIndex); + if (a.hasOption(Opt.GLOB) && vals != null && vals.size() > 0) + { + for (String v : vals) + avs.addValue(val, argIndex++); + } + else + { + avs.addValue(val, argIndex); + } } else if (a.hasOption(Opt.BOOLEAN)) { @@ -632,6 +682,29 @@ public class ArgParser } } + /* + * 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); @@ -1054,9 +1127,9 @@ public class ArgParser { if (!argFile.exists()) { - System.err.println( - "--" + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" - + argFile.getPath() + "\": File does not exist."); + System.err.println(DOUBLEDASH + + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" + + argFile.getPath() + "\": File does not exist."); System.exit(2); } try @@ -1064,7 +1137,7 @@ public class ArgParser argsList.addAll(Files.readAllLines(Paths.get(argFile.getPath()))); } catch (IOException e) { - System.err.println("--" + System.err.println(DOUBLEDASH + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" + argFile.getPath() + "\": File could not be read."); System.exit(3); @@ -1080,56 +1153,66 @@ public class ArgParser public static BootstrapArgs getBootstrapArgs(String[] args) { - return new BootstrapArgs(args); + List argList = new ArrayList<>(Arrays.asList(args)); + return new BootstrapArgs(argList); } - private BootstrapArgs(String[] args) + private BootstrapArgs(List args) { init(args); } - private void init(String[] args) + private void init(List args) { if (args == null) return; - Enumeration argE = Collections - .enumeration(Arrays.asList(args)); - 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; - if (arg.startsWith("--")) + if (arg.startsWith(ArgParser.DOUBLEDASH)) { int equalPos = arg.indexOf('='); if (equalPos > -1) { - argName = arg.substring(2, equalPos); + argName = arg.substring(ArgParser.DOUBLEDASH.length(), + equalPos); val = arg.substring(equalPos + 1); } else { - argName = arg.substring(2); + argName = arg.substring(ArgParser.DOUBLEDASH.length()); val = "true"; } + Arg a = argMap.get(argName); - if (a != null && bootstrapArgs.contains(a)) + + if (a == null || !a.hasOption(Opt.BOOTSTRAP)) + { + // not a valid bootstrap arg + continue; + } + + if (a.hasOption(Opt.STRING)) { - if (!bootstrapArgMap.containsKey(a) - || bootstrapArgMap.get(a) == null) + if (equalPos == -1) { - List aL = new ArrayList<>(); - aL.add(val); - bootstrapArgMap.put(a, aL); + addAll(a, ArgParser.getShellGlobbedFilenameValues(a, args, + i + 1)); } - else if (a.hasOption(Opt.MULTI)) + else { - List aL = bootstrapArgMap.get(a); // already established - // this is not null - aL.add(val); - bootstrapArgMap.put(a, aL); + if (a.hasOption(Opt.GLOB)) + addAll(a, FileUtils.getFilenamesFromGlob(val)); + else + add(a, val); } } + else + { + add(a, val); + } } } } @@ -1144,14 +1227,56 @@ public class ArgParser return bootstrapArgMap.get(a); } - public String get(Arg a) + private List getOrCreateList(Arg a) { - if (bootstrapArgMap.containsKey(a)) + List l = getList(a); + if (l == null) { - List aL = bootstrapArgMap.get(a); - return (aL == null || aL.size() == 0) ? null : aL.get(0); + l = new ArrayList<>(); + putList(a, l); } - return null; + 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