1 package jalview.bin.argparser;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.EnumSet;
6 import java.util.Iterator;
8 import java.util.Locale;
9 import java.util.stream.Collectors;
11 import jalview.bin.Cache;
12 import jalview.util.ChannelProperties;
17 // Initialising arguments (BOOTSTRAP)
18 HELP("Display this help statement", Opt.UNARY, Opt.BOOTSTRAP),
20 "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed.",
21 Opt.UNARY, Opt.BOOTSTRAP),
22 JABAWS("Set a different URL to connect to a JABAWS server.", Opt.STRING,
24 NEWS("Show (or don't show) the news feed.", true, Opt.BOOLEAN,
26 SPLASH("Show (or don't show) the About Jalview splash screen.", true,
27 Opt.BOOLEAN, Opt.BOOTSTRAP),
29 "Show (or don't show) the questionnaire if one is available.",
30 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
31 USAGESTATS("Send (or don't send) initial launch usage stats.", true,
32 Opt.BOOLEAN, Opt.BOOTSTRAP),
34 "Attempt (or don't attempt) to connect to JABAWS web services.",
35 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
36 PROPS("Use a file as the preferences file instead of the usual ~/"
37 + ChannelProperties.getProperty("preferences.filename")
38 + " file.", Opt.STRING, Opt.BOOTSTRAP),
39 DEBUG("Start Jalview in debug log level.", Opt.BOOLEAN, Opt.BOOTSTRAP),
40 TRACE("Start Jalview in trace log level.", Opt.BOOLEAN, Opt.BOOTSTRAP,
42 QUIET("Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
43 Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
45 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
46 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION),
48 // Opening an alignment
49 OPEN("Opens one or more alignment files or URLs in new alignment windows.",
50 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
51 Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT),
52 APPEND("Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
53 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
54 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT),
55 TITLE("Specifies the title for the open alignment window as string.",
56 Opt.STRING, Opt.LINKED),
57 COLOUR("Applies the colour scheme to the open alignment window. Valid values are:\n"
58 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n" + "zappo,\n"
59 + "taylor,\n" + "gecos-flower,\n" + "gecos-blossom,\n"
60 + "gecos-sunset,\n" + "gecos-ocean,\n" + "hydrophobic,\n"
61 + "helix-propensity,\n" + "strand-propensity,\n"
62 + "turn-propensity,\n" + "buried-index,\n" + "nucleotide,\n"
63 + "nucleotide-ambiguity,\n" + "purine-pyrimidine,\n"
64 + "rna-helices,\n" + "t-coffee-scores,\n" + "sequence-id.",
65 Opt.STRING, Opt.LINKED),
66 FEATURES("Add a feature file or URL to the open alignment.", Opt.STRING,
67 Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
68 TREE("Add a tree file or URL to the open alignment.", Opt.STRING,
69 Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
71 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
72 true, Opt.LINKED, Opt.BOOLEAN),
73 ANNOTATIONS("Add an annotations file or URL to the open alignment.",
74 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
76 "Enforces showing (or not showing) alignment annotations.",
77 Opt.BOOLEAN, Opt.LINKED),
78 WRAP("Enforces wrapped (or not wrapped) alignment formatting.",
79 Opt.BOOLEAN, Opt.LINKED),
81 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
82 Opt.UNARY, Opt.LINKED),
84 // Adding a 3D structure
86 "Load a structure file or URL associated with a sequence in the open alignment.\n"
87 + "The sequence to be associated with can be specified with a following --seqid argument, or the subval modifier seqid=ID can be used. A subval INDEX can also be used to specify the INDEX-th sequence in the open alignment.",
88 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
89 SEQID("Specify the sequence name for the preceding --structure to be associated with.",
90 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
91 PAEMATRIX("Add a PAE json matrix file to the preceding --structure.",
92 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
93 TEMPFAC("Set the type of temperature factor. Possible values are:\n"
94 + "default,\n" + "plddt.", Opt.STRING, Opt.LINKED),
96 "Set the structure viewer to use to open the 3d structure file specified in previous --structure to name. Possible values of name are:\n"
97 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
99 Opt.STRING, Opt.LINKED, Opt.MULTI),
101 "Do not show the temperature factor annotation for the preceding --structure.",
102 Opt.UNARY, Opt.LINKED),
103 SHOWSSANNOTATIONS(null, Opt.BOOLEAN, Opt.LINKED),
106 IMAGE("Output an image of the open alignment window. Format is specified by the subval modifier, a following --type argument or guessed from the file extension. Valid formats/extensions are:\n"
107 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
108 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
110 TYPE("Set the image format for the preceding --image to name. Valid values for name are: svg,\n"
111 + "png,\n" + "eps,\n" + "html,\n" + "biojs.", Opt.STRING,
112 Opt.LINKED, Opt.ALLOWALL),
114 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values for name are:\n"
115 + "text,\n" + "lineart.",
116 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
117 OUTPUT("Export the open alignment to file filename. The format name is specified by the subval modifier format=name, a following --format name argument or guessed from the file extension. Valid format names (and file extensions) are:\n"
118 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
119 + "stockholm (sto, stk),\n" + "pir (pir),\n" + "blc (blc),\n"
120 + "amsa (amsa),\n" + "json (json),\n" + "pileup (pileup),\n"
121 + "msf (msf),\n" + "clustal (aln),\n" + "phylip (phy),\n"
122 + "jalview (jvp, jar).", Opt.STRING, Opt.LINKED,
123 Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL, Opt.REQUIREINPUT),
124 FORMAT("Sets the format for the preceding --output file. Valid formats are:\n"
125 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n" + "blc,\n"
126 + "amsa,\n" + "json,\n" + "pileup,\n" + "msf,\n" + "clustal,\n"
127 + "phylip,\n" + "jalview.", Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
128 GROOVY("Process a groovy script in the file for the open alignment.",
129 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
130 BACKUPS("Enable (or disable) writing backup files when saving an ‑‑output file. This applies to the current open alignment. To apply to all ‑‑output and ‑‑image files, use after ‑‑all.",
131 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
133 "Enable (or disable) overwriting of output files without backups enabled. This applies to the current open alignment. To apply to all ‑‑output and ‑‑image files, use after ‑‑all.",
134 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
135 CLOSE("Close the current open alignment window. This occurs after other output arguments. This applies to the current open alignment. To apply to all ‑‑output and ‑‑image files, use after ‑‑all.",
136 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
138 // controlling flow of arguments
139 NEW("Move on to a new alignment window. This will ensure --append will start a new alignment window and other linked arguments will apply to the new alignment window.",
140 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
142 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are {basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
143 + "{dirname}, - the directory (folder) name of the currently --opened file (or first --appended file),\n"
144 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
145 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
146 + "{n} - the value of the index counter (starting at 0).\n"
147 + "{++n} - increase and substitute the value of the index counter,\n"
148 + "{} - the value of the current alignment window default index.",
149 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
150 ARGFILE("Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
151 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
152 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
153 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
154 Opt.ALLOWSUBSTITUTIONS),
155 NPP("Increase the index counter used in argument value substitutions.",
156 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
157 ALL("Apply the following output arguments to all sets of linked arguments.",
158 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
159 QUIT("After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
164 "Allow specific stdout information. For testing purposes only.",
165 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
166 SETPROP("Set an individual Java System property.", Opt.STRING, Opt.MULTI,
167 Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
168 NIL("This argument does nothing on its own, but can be used with linkedIds.",
169 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
171 // private options (inserted during arg processing)
173 "Sets the current value of the argfilename. Inserted before argfilecontents.",
174 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
177 "Unsets the current value of the argfilename. Inserted after argfile contents.",
178 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
180 // these last two have no purpose in the normal Jalview application but are
181 // used by jalview.bin.Launcher to set memory settings. They are not used by
182 // argparser but are here for Usage statement reasons.
184 "Only available with standalone executable jar or jalview.bin.Launcher.\n"
185 + "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
186 + "The equals sign (\"=\") separator must be used with no spaces.",
187 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
189 "Only available with standalone executable jar or jalview.bin.Launcher.\n"
190 + "Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected.\n"
191 + "The equals sign (\"=\") separator must be used with no spaces.",
192 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
196 public static enum Opt
198 BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
199 // false. A default can be given with setOptions(bool, Opt....).
200 // Use ArgParser.isSet(Arg) to see if this arg was not specified.
201 STRING, // This Arg can accept a value either through --arg=value or --arg
203 UNARY, // This Arg is a boolean value, true if present, false if not. Like
204 // BOOLEAN but without the --noarg option.
205 MULTI, // This Arg can be specified multiple times. Multiple values are
206 // stored in the ArgValuesMap (along with their positional index) for
208 LINKED, // This Arg can be linked to others through a --arg[linkedId] or
209 // --arg[linkedId]=value. If no linkedId is specified then the
210 // current default linkedId will be used.
211 NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
212 // first value will be used and subsequent values ignored
214 BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
215 // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
216 // Args and they cannot be linked or contain SubVals. See
217 // jalview.bin.argparser.BootstrapArgs.
218 GLOB, // This Arg can expand wildcard filename "globs" (e.g.
219 // path/*/filename*). If the Arg value is given as --arg filename*
220 // then the shell will have expanded the glob already, but if
221 // specified as --arg=filename* then the Java glob expansion method
222 // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
223 // might be different from the shell expansion rules.
224 NOACTION, // This Arg does not perform a data task, usually used to control
225 // flow in ArgParser.parse(args).
226 ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
227 // SubVals and values.
228 PRIVATE, // This Arg is used internally, and cannot be specified by the
230 SECRET, // This Arg is used by development processes and although it can be
231 // set by the user, it is not displayed to the user.
232 ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
234 INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
235 // linkedId is used, the defaultLinkedIdCounter is
236 // incremented *first*.
237 INPUT, // This Arg counts as an input for REQUIREINPUT
238 REQUIREINPUT, // This Arg can only be applied via --all if there is an
239 // input (i.e. --open or --append)
242 private final String[] argNames;
244 private Opt[] argOptions;
246 private boolean defaultBoolValue = false;
248 private String description = null;
250 private Arg(String description, Opt... options)
252 this(null, description, false, options);
255 private Arg(String description, boolean defaultBoolean, Opt... options)
257 this(null, description, defaultBoolean, options);
260 private Arg(String alternativeName, String description, Opt... options)
262 this(alternativeName, description, false, options);
265 private Arg(String alternativeName, String description,
266 boolean defaultBoolean, Opt... options)
268 this.argNames = alternativeName != null
270 { this.getName(), alternativeName }
273 this.description = description;
274 this.defaultBoolValue = defaultBoolean;
275 this.setOptions(options);
278 public String argString()
280 return argString(false);
283 public String negateArgString()
285 return argString(true);
288 private String argString(boolean negate)
290 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
291 if (negate && hasOption(Opt.BOOLEAN))
292 sb.append(ArgParser.NEGATESTRING);
293 sb.append(getName());
294 return sb.toString();
297 public String toLongString()
299 StringBuilder sb = new StringBuilder();
300 sb.append(this.getClass().getName()).append('.').append(this.name());
302 if (getNames().length > 0)
304 sb.append(String.join("\", \"", getNames()));
305 if (getNames().length > 0)
308 sb.append("\nOpt: ");
309 // map List<Opt> to List<String> for the String.join
310 List<String> optList = Arrays.asList(argOptions).stream()
311 .map(opt -> opt.name()).collect(Collectors.toList());
312 sb.append(String.join(", ", optList));
314 return sb.toString();
317 public String[] getNames()
322 public String getName()
324 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
328 public final String toString()
333 public boolean hasOption(Opt o)
335 if (argOptions == null)
337 for (Opt option : argOptions)
345 protected void setOptions(Opt... options)
347 this.argOptions = options;
350 protected boolean getDefaultBoolValue()
352 return defaultBoolValue;
355 protected String getDescription()
360 public static String booleanArgString(Arg a)
362 StringBuilder sb = new StringBuilder(a.argString());
363 if (a.hasOption(Opt.BOOLEAN))
366 sb.append(a.negateArgString());
368 return sb.toString();
371 public static final String usage()
373 StringBuilder sb = new StringBuilder();
375 sb.append(ChannelProperties.getProperty("app_name"));
376 String version = Cache.getDefault("VERSION", null);
379 sb.append(" version ");
380 sb.append(Cache.getDefault("VERSION", "unknown"));
382 sb.append(System.lineSeparator());
383 sb.append("Usage: jalview [files...] [args]");
384 sb.append(System.lineSeparator());
385 sb.append(System.lineSeparator());
387 int maxArgLength = 0;
388 for (Arg a : EnumSet.allOf(Arg.class))
390 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
393 StringBuilder argSb = new StringBuilder();
394 argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
396 if (a.hasOption(Opt.STRING))
397 argSb.append("=value");
398 if (argSb.length() > maxArgLength)
399 maxArgLength = argSb.length();
402 // might want to sort these
403 for (Arg a : EnumSet.allOf(Arg.class))
405 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
407 StringBuilder argSb = new StringBuilder();
408 argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
410 if (a.hasOption(Opt.STRING))
411 argSb.append("=value");
412 Iterator<String> descLines = null;
413 if (a.getDescription() != null)
415 descLines = Arrays.stream(a.getDescription().split("\\n"))
418 sb.append(String.format("%-" + maxArgLength + "s", argSb.toString()));
419 boolean first = true;
420 if (descLines != null)
422 while (descLines.hasNext())
427 sb.append(" ".repeat(maxArgLength + 3));
428 sb.append(descLines.next());
429 sb.append(System.lineSeparator());
434 List<String> options = new ArrayList<>();
436 if (a.hasOption(Opt.BOOLEAN))
438 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
439 : a.negateArgString()));
442 if (a.hasOption(Opt.MULTI))
444 options.add("multiple");
447 if (a.hasOption(Opt.LINKED))
449 options.add("can be linked");
452 if (a.hasOption(Opt.GLOB))
454 options.add("allows file globs");
457 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
459 options.add("allows substitutions");
462 if (a.hasOption(Opt.PRIVATE))
464 options.add("for internal use only");
467 if (a.hasOption(Opt.SECRET))
469 options.add("for development use only");
472 if (options.size() > 0)
477 sb.append(" ".repeat(maxArgLength + 3));
479 sb.append(String.join("; ", options));
481 sb.append(System.lineSeparator());
483 sb.append(System.lineSeparator());
485 return sb.toString();