X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fbin%2FArgParser.java;h=9af5e4ad944069a9f384e4037b3e3c84472b12af;hb=2db5d47c785c67cdb7a82b609b3a7eb8af4e7e0c;hp=cc31f5bc8715eff4697b6c1963117be6308195b3;hpb=0fbfbe484308a0a6d9663f69f287cc61cfba958a;p=jalview.git diff --git a/src/jalview/bin/ArgParser.java b/src/jalview/bin/ArgParser.java index cc31f5b..9af5e4a 100644 --- a/src/jalview/bin/ArgParser.java +++ b/src/jalview/bin/ArgParser.java @@ -20,30 +20,47 @@ */ 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.Collection; -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.Platform; +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, ORDERED, NODUPLICATEVALUES + BOOLEAN, STRING, UNARY, MULTI, LINKED, NODUPLICATEVALUES, BOOTSTRAP, + GLOB } public enum Arg @@ -61,7 +78,8 @@ public class ArgParser 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, DEBUG("d"); + NOSTRUCTURE, STRUCTURE, IMAGE, QUIT, CLOSE, DEBUG("d"), QUIET("q"), + ARGFILE, INCREMENT, NPP("n++"); static { @@ -76,7 +94,7 @@ public class ArgParser FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI); GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI); GROUPS.setOptions(Opt.STRING, Opt.LINKED); - HEADLESS.setOptions(Opt.UNARY); + HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP); JABAWS.setOptions(Opt.STRING); ANNOTATION.setOptions(true, Opt.BOOLEAN); ANNOTATION2.setOptions(true, Opt.BOOLEAN); @@ -87,9 +105,9 @@ 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); + PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP); QUESTIONNAIRE.setOptions(Opt.STRING); SETPROP.setOptions(Opt.STRING); TREE.setOptions(Opt.STRING); @@ -113,7 +131,16 @@ public class ArgParser WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED); IMAGE.setOptions(Opt.STRING, Opt.LINKED); QUIT.setOptions(Opt.UNARY); - DEBUG.setOptions(Opt.BOOLEAN); + 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); + 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; @@ -409,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; @@ -433,7 +450,7 @@ public class ArgParser argMap = new HashMap<>(); for (Arg a : EnumSet.allOf(Arg.class)) { - ARGNAME: for (String argName : a.getNames()) + for (String argName : a.getNames()) { if (argMap.containsKey(argName)) { @@ -447,7 +464,7 @@ public class ArgParser + "' and '" + a.getName() + ":" + argName + "'"); } - continue ARGNAME; + continue; } argMap.put(argName, a); } @@ -456,53 +473,39 @@ public class ArgParser public ArgParser(String[] args) { - // old style - vargs = new ArrayList<>(); - isApplet = (args.length > 0 && args[0].startsWith("(Arrays.asList(args))); + } - { - isApplet = true; - // appletParams = - // AppletParams.getAppletParams(Platform.getAppletInfoAsMap(), vargs); - } - for (int i = 0; i < args.length; i++) - { - String arg = args[i].trim(); - if (arg.charAt(0) == '-') - { - arg = arg.substring(1); - } - vargs.add(arg); - } - } + public ArgParser(List args) + { + init(args); + } - // new style - Enumeration argE = Collections.enumeration(Arrays.asList(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(']'); @@ -558,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 @@ -603,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)) { @@ -621,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<>(); @@ -638,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); @@ -783,6 +874,8 @@ public class ArgParser public void parseVals(String item) { + if (item == null) + return; if (item.indexOf('[') == 0 && item.indexOf(']') > 1) { int openBracket = item.indexOf('['); @@ -934,7 +1027,8 @@ public class ArgParser { if (m == null || !m.containsKey(a)) return false; - return getArgValue(a) != null; + return a.hasOption(Opt.STRING) ? getArgValue(a) != null + : this.getBoolean(a); } protected boolean hasValue(Arg a, String val) @@ -1037,37 +1131,176 @@ public class ArgParser } } - private static final Collection bootstrapArgs = new ArrayList( - Arrays.asList(Arg.PROPS, Arg.DEBUG)); + 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)); + } - public static Map bootstrapArgs(String[] args) + return parseArgFileList(argFiles); + } + + public static ArgParser parseArgFileList(List argFiles) { - Map bootstrapArgMap = new HashMap<>(); - if (args == null) - return bootstrapArgMap; - Enumeration argE = Collections.enumeration(Arrays.asList(args)); - while (argE.hasMoreElements()) + List argsList = new ArrayList<>(); + for (File argFile : argFiles) { - String arg = argE.nextElement(); - String argName = null; - String val = null; - if (arg.startsWith("--")) + if (!argFile.exists()) { - int equalPos = arg.indexOf('='); - if (equalPos > -1) - { - argName = arg.substring(2, equalPos); - val = arg.substring(equalPos + 1); - } - else + 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(argFile.getPath()))); + } catch (IOException e) + { + System.err.println(DOUBLEDASH + + Arg.ARGFILE.name().toLowerCase(Locale.ROOT) + "=\"" + + argFile.getPath() + "\": File could not be read."); + System.exit(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)) { - argName = arg.substring(2); + 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); + } } - Arg a = argMap.get(argName); - if (a != null && bootstrapArgs.contains(a)) - bootstrapArgMap.put(a, val); } } - return bootstrapArgMap; + + 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