package jalview.bin.argparser; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; public enum Arg { HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, SHOWANNOTATIONS, COLOUR("color"), FEATURES, ANNOTATIONS, GROOVY, GROUPS, HEADLESS, JABAWS, DISPLAY, NEWS, SORTBYTREE, USAGESTATS, APPEND, OPEN, PROPS, QUESTIONNAIRE, SETPROP, TREE, VDOC, VSESS, OUTPUT, SSANNOTATIONS, NOTEMPFAC, TEMPFAC, TITLE, PAEMATRIX, WRAP, NOSTRUCTURE, STRUCTURE, STRUCTUREVIEWER, IMAGE, TYPE, FORMAT, OVERWRITE, TEXTRENDERER, QUIT, CLOSE, DEBUG("d"), TRACE, QUIET("q"), ARGFILE, NEW, NPP("n++"), SUBSTITUTIONS, INITSUBSTITUTIONS, NIL, SPLASH, SETARGFILE, UNSETARGFILE, WEBSERVICEDISCOVERY, ALL, BACKUPS, TESTOUTPUT, SEQID; public static enum Opt { BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or // false. A default can be given with setOptions(bool, Opt....). // Use ArgParser.isSet(Arg) to see if this arg was not specified. STRING, // This Arg can accept a value either through --arg=value or --arg // value. UNARY, // This Arg is a boolean value, true if present, false if not. Like // BOOLEAN but without the --noarg option. MULTI, // This Arg can be specified multiple times. Multiple values are // stored in the ArgValuesMap (along with their positional index) for // each linkedId. LINKED, // This Arg can be linked to others through a --arg[linkedId] or // --arg[linkedId]=value. If no linkedId is specified then the // current default linkedId will be used. NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The // first value will be used and subsequent values ignored // with a warning. BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP // Args and they cannot be linked or contain SubVals. See // jalview.bin.argparser.BootstrapArgs. GLOB, // This Arg can expand wildcard filename "globs" (e.g. // path/*/filename*). If the Arg value is given as --arg filename* // then the shell will have expanded the glob already, but if // specified as --arg=filename* then the Java glob expansion method // will be used (see FileUtils.getFilenamesFromGlob()). Note that this // might be different from the shell expansion rules. NOACTION, // This Arg does not perform a data task, usually used to control // flow in ArgParser.parse(args). ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId, // SubVals and values. PRIVATE, // This Arg is used internally, and cannot be specified by the // user. ALLOWALL, // This Arg can use the '*' linkedId to apply to all known // linkedIds INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default // linkedId is used, the defaultLinkedIdCounter is // incremented *first*. INPUT, // This Arg counts as an input for REQUIREINPUT REQUIREINPUT, // This Arg can only be applied via --all if there is an // input (i.e. --open or --append) } static { HELP.setOptions("Display this help message", Opt.UNARY, Opt.BOOTSTRAP); CALCULATION.setOptions(true, Opt.BOOLEAN); // default "true" implies only // expecting "--nocalculation" MENUBAR.setOptions(true, Opt.BOOLEAN); STATUS.setOptions(true, Opt.BOOLEAN); SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED); SHOWANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED); COLOUR.setOptions(Opt.STRING, Opt.LINKED); FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); ANNOTATIONS.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); TREE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); GROUPS.setOptions(Opt.STRING, Opt.LINKED); HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP); TESTOUTPUT.setOptions(Opt.UNARY, Opt.BOOTSTRAP); JABAWS.setOptions(Opt.STRING, Opt.BOOTSTRAP); DISPLAY.setOptions(true, Opt.BOOLEAN); NEWS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); SPLASH.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); SORTBYTREE.setOptions(true, Opt.LINKED, Opt.BOOLEAN); QUESTIONNAIRE.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); USAGESTATS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); WEBSERVICEDISCOVERY.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); APPEND.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT); OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT); PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP); SETPROP.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP); VDOC.setOptions(Opt.UNARY); VSESS.setOptions(Opt.UNARY); OUTPUT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL, Opt.REQUIREINPUT); SSANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED); NOTEMPFAC.setOptions(Opt.UNARY, Opt.LINKED); TEMPFAC.setOptions(Opt.STRING, Opt.LINKED); TITLE.setOptions(Opt.STRING, Opt.LINKED); PAEMATRIX.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); SEQID.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED); STRUCTURE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); STRUCTUREVIEWER.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI); WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED); IMAGE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL, Opt.REQUIREINPUT); TYPE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL); FORMAT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL); TEXTRENDERER.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL); QUIT.setOptions(Opt.UNARY); OVERWRITE.setOptions(Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL); BACKUPS.setOptions(true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL); CLOSE.setOptions(Opt.UNARY, Opt.LINKED, Opt.ALLOWALL); DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP); TRACE.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP); QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP); ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS); NEW.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER); NPP.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION); SUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION); INITSUBSTITUTIONS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION); // defaulting substitutions to true NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION); SETARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION); UNSETARGFILE.setOptions(Opt.MULTI, Opt.PRIVATE, Opt.NOACTION); ALL.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION); } private final String[] argNames; private Opt[] argOptions; private boolean defaultBoolValue = false; private String description = null; private Arg() { this(new String[0]); } private Arg(String... names) { int length = (names == null || names.length == 0 || (names.length == 1 && names[0] == null)) ? 1 : names.length + 1; this.argNames = new String[length]; this.argNames[0] = this.getName(); if (length > 1) System.arraycopy(names, 0, this.argNames, 1, names.length); } public String argString() { return argString(false); } public String negateArgString() { return argString(true); } private String argString(boolean negate) { StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH); if (negate && hasOption(Opt.BOOLEAN)) sb.append(ArgParser.NEGATESTRING); sb.append(getName()); return sb.toString(); } public String toLongString() { StringBuilder sb = new StringBuilder(); sb.append(this.getClass().getName()).append('.').append(this.name()); sb.append('('); if (getNames().length > 0) sb.append('"'); sb.append(String.join("\", \"", getNames())); if (getNames().length > 0) sb.append('"'); sb.append(")\n"); sb.append("\nOpt: "); // map List to List for the String.join List optList = Arrays.asList(argOptions).stream() .map(opt -> opt.name()).collect(Collectors.toList()); sb.append(String.join(", ", optList)); sb.append("\n"); return sb.toString(); } public String[] getNames() { return argNames; } public String getName() { return this.name().toLowerCase(Locale.ROOT).replace('_', '-'); } @Override public final String toString() { return getName(); } public boolean hasOption(Opt o) { if (argOptions == null) return false; for (Opt option : argOptions) { if (o == option) return true; } return false; } protected void setOptions(Opt... options) { setOptions("", false, options); } protected void setOptions(String desc, Opt... options) { setOptions(desc, false, options); } protected void setOptions(boolean defaultBoolValue, Opt... options) { setOptions("", defaultBoolValue, options); } protected void setOptions(String desc, boolean defaultBoolValue, Opt... options) { this.description = desc; this.defaultBoolValue = defaultBoolValue; this.argOptions = options; } protected boolean getDefaultBoolValue() { return defaultBoolValue; } private void setDescription(String d) { description = d; } protected String getDescription() { return description; } public static String booleanArgString(Arg a) { StringBuilder sb = new StringBuilder(a.argString()); if (a.hasOption(Opt.BOOLEAN)) { sb.append('/'); sb.append(a.negateArgString()); } return sb.toString(); } public static final String usage() { StringBuilder sb = new StringBuilder(); sb.append("Usage: jalview [args]"); sb.append(System.lineSeparator()); int maxArgLength = 0; for (Arg a : EnumSet.allOf(Arg.class)) { if (a.hasOption(Opt.PRIVATE)) continue; StringBuilder argSb = new StringBuilder(); argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString()); if (a.hasOption(Opt.STRING)) argSb.append("=value"); if (argSb.length() > maxArgLength) maxArgLength = argSb.length(); } // might want to sort these for (Arg a : EnumSet.allOf(Arg.class)) { if (a.hasOption(Opt.PRIVATE)) continue; StringBuilder argSb = new StringBuilder(); argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString()); if (a.hasOption(Opt.STRING)) argSb.append("=value"); sb.append(String.format("%-" + maxArgLength + "s - %s", argSb.toString(), a.getDescription())); List options = new ArrayList<>(); if (a.hasOption(Opt.BOOLEAN)) { options.add("default " + (a.getDefaultBoolValue() ? a.argString() : a.negateArgString())); } if (a.hasOption(Opt.MULTI)) { options.add("multiple"); } if (a.hasOption(Opt.LINKED)) { options.add("can be linked"); } if (a.hasOption(Opt.GLOB)) { options.add("allows file globs"); } if (a.hasOption(Opt.ALLOWSUBSTITUTIONS)) { options.add("allows substitutions"); } if (options.size() > 0) { sb.append(" ("); sb.append(String.join("; ", options)); sb.append(')'); } sb.append(System.lineSeparator()); } return sb.toString(); } }