X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fbin%2Fargparser%2FArgParser.java;h=e08ae035840c72597eb822086086f30b30b4193a;hb=6dafac1b9bcc7265b0fb1641f7474a6c81b24119;hp=f22ca7f3d8a195ac0209743c80d89af71e96329a;hpb=a16741c87b8246f228af144f52bcff904d66e43a;p=jalview.git diff --git a/src/jalview/bin/argparser/ArgParser.java b/src/jalview/bin/argparser/ArgParser.java index f22ca7f..e08ae03 100644 --- a/src/jalview/bin/argparser/ArgParser.java +++ b/src/jalview/bin/argparser/ArgParser.java @@ -32,15 +32,19 @@ import java.util.List; 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) @@ -49,20 +53,44 @@ public class ArgParser // 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 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 used to increment the idCounter (and use the incremented - // value) - private static final String INCREMENTAUTOCOUNTERLINKEDID = "{++n}"; + // the linked id substitution string used to use the dir path of --append + // or --open + private static final String LINKEDIDDIRNAME = "{dirname}"; - // the linked id used to use the idCounter - private static final String AUTOCOUNTERLINKEDID = "{n}"; + // the current argfile + private String argFile = null; - private int idCounter = 0; + // 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 @@ -76,6 +104,8 @@ public class ArgParser protected List argList; + private static final char ARGFILECOMMENT = '#'; + static { argMap = new HashMap<>(); @@ -86,7 +116,8 @@ public class ArgParser 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( @@ -104,39 +135,73 @@ public class ArgParser 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 args) + public ArgParser(List args, boolean initsubstitutions) { - parse(args); + this(args, initsubstitutions, false); } - private void parse(List args) + public ArgParser(List args, boolean initsubstitutions, + boolean allowPrivate) { + // do nothing if there are no "--" args and (some "-" args || >0 arg is + // "open") + boolean d = false; + boolean dd = false; + for (String arg : args) + { + if (arg.startsWith(DOUBLEDASH)) + { + dd = true; + break; + } + 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(), false, false); + return; + } + parse(args, initsubstitutions, allowPrivate); + } + + private void parse(List args, boolean initsubstitutions, + boolean allowPrivate) + { + this.substitutions = initsubstitutions; int argIndex = 0; boolean openEachInitialFilenames = true; for (int i = 0; i < args.size(); i++) { String arg = args.get(i); - Console.debug("##### Looking at arg '" + arg + "'"); - // If the first arguments do not start with "--" or "-" or is "open" and // is a filename that exists it is probably a file/list of files to open // so we fake an Arg.OPEN argument and when adding files only add the // 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 { @@ -145,11 +210,12 @@ public class ArgParser String argName = null; String val = null; - List vals = null; // for Opt.GLOB only + List globVals = null; // for Opt.GLOB only + SubVals globSubVals = null; // also for use by Opt.GLOB only 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); @@ -186,17 +252,23 @@ public class ArgParser 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; @@ -204,54 +276,83 @@ public class ArgParser 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.STRING) && equalPos == -1) + // String value(s) + if (a.hasOption(Opt.STRING)) { - // take next arg as value if required, and '=' was not found - // if (!argE.hasMoreElements()) - if (i + 1 >= args.size()) + if (equalPos >= 0) { - // no value to take for arg, which wants a value - Console.error("Argument '" + a.getName() - + "' requires a value, none given. Ignoring."); - continue; - } - // 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)) - { - // if this is the first argument with a file list at the start of - // the args we add filenames from index i instead of i+1 - vals = getShellGlobbedFilenameValues(a, args, - openEachInitialFilenames ? i : i + 1); + if (a.hasOption(Opt.GLOB)) + { + // strip off and save the SubVals to be added individually later + globSubVals = new SubVals(val); + // make substitutions before looking for files + String fileGlob = makeSubstitutions(globSubVals.getContent(), + linkedId); + globVals = FileUtils.getFilenamesFromGlob(fileGlob); + } + else + { + // val is already set -- will be saved in the ArgValue later in + // the normal way + } } else { - val = args.get(i + 1); + // There is no "=" so value is next arg or args (possibly shell + // glob-expanded) + if ((openEachInitialFilenames ? i : 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; + } + // 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)) + { + // if this is the first argument with a file list at the start of + // the args we add filenames from index i instead of i+1 + globVals = getShellGlobbedFilenameValues(a, args, + openEachInitialFilenames ? i : i + 1); + } + else + { + val = args.get(i + 1); + } } } // make NOACTION adjustments // default and auto counter increments - if (a == Arg.INCREMENT) + if (a == Arg.NEWFRAME) { defaultLinkedIdCounter++; } else if (a == Arg.NPP) { - idCounter++; + linkedIdAutoCounter++; } else if (a == Arg.SUBSTITUTIONS) { substitutions = !negated; } + else if (a == Arg.SETARGFILE) + { + argFile = val; + } + else if (a == Arg.UNSETARGFILE) + { + argFile = null; + } String autoCounterString = null; boolean usingAutoCounterLinkedId = false; @@ -259,37 +360,44 @@ public class ArgParser .append(Integer.toString(defaultLinkedIdCounter)) .toString(); boolean usingDefaultLinkedId = false; - if (a == Arg.OPENNEW) - { - linkedId = new StringBuilder(OPENNEWLINKEDIDPREFIX) - .append(Integer.toString(opennewLinkedIdCounter)) - .toString(); - opennewLinkedIdCounter++; - } - else if (a.hasOption(Opt.LINKED)) + 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); + if (a == Arg.OPEN) + { + // use the next default prefixed OPENLINKEDID + // NOW using the linkedIdAutoCounter + defaultLinkedIdCounter++; + linkedId = new StringBuilder(OPENLINKEDIDPREFIX) + .append(Integer.toString(defaultLinkedIdCounter)) + .toString(); + } + else + { + // use default linkedId for linked arguments + linkedId = defaultLinkedId; + usingDefaultLinkedId = true; + Console.debug("Changing linkedId to '" + linkedId + "' from " + + arg); + } } - else if (linkedId.equals(AUTOCOUNTERLINKEDID)) + else if (linkedId.contains(LINKEDIDAUTOCOUNTER)) { // turn {n} to the autoCounter - autoCounterString = Integer.toString(idCounter); - linkedId = autoCounterString; + autoCounterString = Integer.toString(linkedIdAutoCounter); + linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER, + autoCounterString); usingAutoCounterLinkedId = true; Console.debug( "Changing linkedId to '" + linkedId + "' from " + arg); } - else if (linkedId.equals(INCREMENTAUTOCOUNTERLINKEDID)) + else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER)) { // turn {++n} to the incremented autoCounter - autoCounterString = Integer.toString(++idCounter); - linkedId = autoCounterString; + autoCounterString = Integer.toString(++linkedIdAutoCounter); + linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER, + autoCounterString); usingAutoCounterLinkedId = true; Console.debug( "Changing linkedId to '" + linkedId + "' from " + arg); @@ -308,39 +416,42 @@ public class ArgParser // 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 sv = ArgParser.getSubVals(val); - String id = sv.get(ArgValues.ID); + 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; ArgValues avs = avm.getOrCreateArgValues(a); - // store appropriate value + // store appropriate String value(s) if (a.hasOption(Opt.STRING)) { - if (a.hasOption(Opt.GLOB) && vals != null && vals.size() > 0) + if (a.hasOption(Opt.GLOB) && globVals != null + && globVals.size() > 0) { - for (String v : vals) + for (String v : globVals) { - avs.addValue(makeSubstitutions(v), argIndex++); + v = makeSubstitutions(v, linkedId); + SubVals vsv = new SubVals(globSubVals, v); + avs.addValue(vsv, v, argIndex++); argIndexIncremented = true; } } else { - avs.addValue(makeSubstitutions(val), argIndex); + avs.addValue(makeSubstitutions(val, linkedId), argIndex); } } else if (a.hasOption(Opt.BOOLEAN)) @@ -371,13 +482,14 @@ public class ArgParser argList = new ArrayList<>(); if (!argList.contains(a)) argList.add(a); + } } } - private String makeSubstitutions(String val) + public String makeSubstitutions(String val, String linkedId) { - if (!this.substitutions) + if (!this.substitutions || val == null) return val; String subvals; @@ -395,9 +507,40 @@ public class ArgParser subvals = ""; rest = val; } - rest.replace(AUTOCOUNTERLINKEDID, String.valueOf(idCounter)); - rest.replace(INCREMENTAUTOCOUNTERLINKEDID, String.valueOf(++idCounter)); - rest.replace("{}", String.valueOf(defaultLinkedIdCounter)); + if (rest.contains(LINKEDIDAUTOCOUNTER)) + rest = rest.replace(LINKEDIDAUTOCOUNTER, + String.valueOf(linkedIdAutoCounter)); + if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER)) + rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER, + String.valueOf(++linkedIdAutoCounter)); + 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(); } @@ -405,12 +548,11 @@ 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. + * 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. */ protected static List getShellGlobbedFilenameValues(Arg a, List args, int i) @@ -418,7 +560,7 @@ public class ArgParser List vals = new ArrayList<>(); while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH)) { - vals.add(args.remove(i)); + vals.add(FileUtils.substituteHomeDir(args.remove(i))); if (!a.hasOption(Opt.GLOB)) break; } @@ -502,12 +644,8 @@ public class ArgParser return sb.toString(); } - public static SubVals getSubVals(String item) - { - return new SubVals(item); - } - - public static ArgParser parseArgFiles(List argFilenameGlobs) + public static ArgParser parseArgFiles(List argFilenameGlobs, + boolean initsubstitutions) { List argFiles = new ArrayList<>(); @@ -517,33 +655,122 @@ public class ArgParser argFiles.addAll(FileUtils.getFilesFromGlob(pattern)); } - return parseArgFileList(argFiles); + return parseArgFileList(argFiles, initsubstitutions); } - public static ArgParser parseArgFileList(List argFiles) + public static ArgParser parseArgFileList(List argFiles, + boolean initsubstitutions) { List 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 readArgFile(File argFile) + { + List 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; } } \ No newline at end of file