X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fbin%2FArgParser.java;h=9af5e4ad944069a9f384e4037b3e3c84472b12af;hb=2db5d47c785c67cdb7a82b609b3a7eb8af4e7e0c;hp=65d00266fc627cb4ae9eeebd34e13d46a93f41b2;hpb=71fc2ec36ecf51f66225d9b98eb475ae657bef05;p=jalview.git diff --git a/src/jalview/bin/ArgParser.java b/src/jalview/bin/ArgParser.java index 65d0026..9af5e4a 100644 --- a/src/jalview/bin/ArgParser.java +++ b/src/jalview/bin/ArgParser.java @@ -27,32 +27,42 @@ 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; 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"; - private static final String DEFAULTLINKEDID = ""; + // 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; private static enum Opt { - BOOLEAN, STRING, UNARY, MULTI, LINKED, NODUPLICATEVALUES, BOOTSTRAP + 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 { /* @@ -69,7 +79,7 @@ public class ArgParser 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; + ARGFILE, INCREMENT, NPP("n++"); static { @@ -95,7 +105,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); @@ -124,7 +134,13 @@ public class ArgParser 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); + ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB); + INCREMENT.setOptions(Opt.UNARY, Opt.MULTI); + NPP.setOptions(Opt.UNARY, Opt.MULTI); + // 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; @@ -420,16 +436,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; @@ -442,11 +448,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)) @@ -470,30 +473,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(']'); @@ -549,25 +561,82 @@ 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; + // default and auto counter increments + if (a == Arg.INCREMENT) + { + defaultLinkedIdCounter++; + continue; + } + else if (a == Arg.NPP) + { + idCounter++; + continue; + } + + 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()); 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 @@ -594,7 +663,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)) { @@ -612,7 +689,7 @@ public class ArgParser { // allow a default linked id for single usage if (linkedId == null) - linkedId = DEFAULTLINKEDID; + linkedId = defaultLinkedId; // store the order of linkedIds if (linkedOrder == null) linkedOrder = new ArrayList<>(); @@ -629,6 +706,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); @@ -1031,27 +1131,39 @@ public class ArgParser } } - public static ArgParser parseArgFiles(List argFilenames) + 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 (String argFilename : argFilenames) + for (File argFile : argFiles) { - File argFile = new File(argFilename); if (!argFile.exists()) { - System.err - .println("--" + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) - + "=\"" + argFilename + "\": File does not exist."); + System.err.println(DOUBLEDASH + + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" + + argFile.getPath() + "\": File does not exist."); System.exit(2); } try { - argsList.addAll(Files.readAllLines(Paths.get(argFilename))); + argsList.addAll(Files.readAllLines(Paths.get(argFile.getPath()))); } catch (IOException e) { - System.err.println( - "--" + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" - + argFilename + "\": File could not be read."); + System.err.println(DOUBLEDASH + + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" + + argFile.getPath() + "\": File could not be read."); System.exit(3); } } @@ -1063,58 +1175,68 @@ public class ArgParser // only need one private static Map> bootstrapArgMap = new HashMap<>(); - private BootstrapArgs(String[] args) + public static BootstrapArgs getBootstrapArgs(String[] args) { - init(args); + List argList = new ArrayList<>(Arrays.asList(args)); + return new BootstrapArgs(argList); } - public static BootstrapArgs getBootstrapArgs(String[] args) + private BootstrapArgs(List args) { - return new BootstrapArgs(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)) { - if (!bootstrapArgMap.containsKey(a) - || bootstrapArgMap.get(a) == null) + // not a valid bootstrap arg + continue; + } + + if (a.hasOption(Opt.STRING)) + { + 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); + } } } } @@ -1129,14 +1251,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