2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.bin.argparser;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.EnumSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.stream.Collectors;
33 import jalview.bin.argparser.Arg.Opt;
34 import jalview.util.ChannelProperties;
35 import jalview.util.Platform;
40 // Initialising arguments (BOOTSTRAP)
41 HELP(Type.HELP, "h", "Display basic help", Opt.UNARY, Opt.BOOTSTRAP,
42 Opt.HASTYPE, Opt.MULTIVALUE),
44 * Other --help-type Args will be added by the static block.
46 VERSION(Type.CONFIG, "v",
47 "Display the version of "
48 + ChannelProperties.getProperty("app_name"),
49 Opt.UNARY, Opt.BOOTSTRAP),
51 "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed. "
52 + "Headless mode is assumed if an output file is to be generated, this can be overridden with --gui.",
53 Opt.UNARY, Opt.BOOTSTRAP),
55 "Do not run Jalview in headless mode. This overrides the assumption of headless mode when an output file is to be generated.",
56 Opt.UNARY, Opt.BOOTSTRAP),
57 JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
58 Opt.STRING, Opt.BOOTSTRAP),
59 NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
60 Opt.BOOLEAN, Opt.BOOTSTRAP),
62 "Show (or don't show) the About Jalview splash screen.", true,
63 Opt.BOOLEAN, Opt.BOOTSTRAP),
64 QUESTIONNAIRE(Type.CONFIG,
65 "Show (or don't show) the questionnaire if one is available.",
66 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
67 JAVACONSOLE(Type.CONFIG, "Show (or don't show) the Java Console.", false,
68 Opt.BOOLEAN, Opt.BOOTSTRAP),
69 NOUSAGESTATS(Type.CONFIG, "Don't send initial launch usage stats.",
70 Opt.UNARY, Opt.BOOTSTRAP),
71 NOSTARTUPFILE(Type.CONFIG, "Don't show the default startup file.",
72 Opt.UNARY, Opt.BOOTSTRAP),
73 WEBSERVICEDISCOVERY(Type.CONFIG,
74 "Attempt (or don't attempt) to connect to JABAWS web services.",
75 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
77 "Use a file as the preferences file instead of the usual ~/"
78 + ChannelProperties.getProperty("preferences.filename")
80 Opt.STRING, Opt.BOOTSTRAP),
81 DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
83 TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
84 Opt.BOOTSTRAP, Opt.SECRET),
85 QUIET(Type.CONFIG, "q",
86 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
87 Opt.UNARY, Opt.MULTIVALUE, Opt.BOOTSTRAP),
88 INITSUBSTITUTIONS(Type.CONFIG,
89 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
90 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
91 P(Type.CONFIG, "Set a Jalview preference value for this session.",
92 Opt.PREFIXKEV, Opt.PRESERVECASE, Opt.STRING, Opt.BOOTSTRAP,
93 Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET), // keep this secret for
96 // Opening an alignment
98 "Opens one or more alignment files or URLs in new alignment windows.",
99 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER,
100 Opt.MULTIVALUE, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT,
101 Opt.STORED, Opt.PRIMARY),
103 "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
104 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.GLOB,
105 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
107 "Specifies the title for the open alignment window as string.",
108 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
109 COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
110 "Applies the colour scheme to the open alignment window. Valid values include:\n"
111 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
112 + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
113 + "gecos-blossom,\n" + "gecos-sunset,\n"
114 + "gecos-ocean,\n" + "hydrophobic,\n"
115 + "helix-propensity,\n" + "strand-propensity,\n"
116 + "turn-propensity,\n" + "buried-index,\n"
117 + "nucleotide,\n" + "nucleotide-ambiguity,\n"
118 + "purine-pyrimidine,\n" + "rna-helices,\n"
119 + "t-coffee-scores,\n" + "sequence-id.\n" + "\n"
120 + "Names of user defined colourschemes will also work,\n"
121 + "and jalview colourscheme specifications like\n"
122 + "--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
123 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
124 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
125 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
127 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
128 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
130 SORTBYTREE(Type.OPENING,
131 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
132 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWMULTIID),
133 ANNOTATIONS(Type.OPENING,
134 "Add an annotations file or URL to the open alignment.",
135 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
137 SHOWANNOTATIONS(Type.OPENING,
138 "Enforces showing (or not showing) alignment annotations.",
139 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
141 "Enforces wrapped (or not wrapped) alignment formatting.",
142 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
143 NOSTRUCTURE(Type.OPENING,
144 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
145 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
147 // Adding a 3D structure
148 STRUCTURE(Type.STRUCTURE,
149 "Load a structure file or URL associated with a sequence in the open alignment.\n"
150 + "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.",
151 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
152 Opt.PRIMARY, Opt.ALLOWMULTIID),
153 SEQID(Type.STRUCTURE,
154 "Specify the sequence name for the preceding --structure to be associated with.",
155 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
157 PAEMATRIX(Type.STRUCTURE,
158 "Add a PAE json matrix file to the preceding --structure.",
159 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
161 TEMPFAC(Type.STRUCTURE,
162 "Set the type of temperature factor. Possible values are:\n"
163 + "default,\n" + "plddt.",
164 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
165 STRUCTUREVIEWER(Type.STRUCTURE,
166 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
167 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
169 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
170 NOTEMPFAC(Type.STRUCTURE,
171 "Do not show the temperature factor annotation for the preceding --structure.",
172 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.SECRET), // keep this
175 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
180 "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"
181 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
182 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.MULTIVALUE,
183 Opt.ALLOWMULTIID, Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
184 STRUCTUREIMAGE(Type.IMAGE,
185 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
186 Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID,
189 "Set the image format for the preceding " + Arg.IMAGE.argString()
190 + " or " + Arg.STRUCTUREIMAGE.argString()
191 + ". Valid values are:\n" + "svg,\n" + "png,\n" + "eps,\n"
192 + "html,\n" + "biojs.",
193 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
194 TEXTRENDERER(Type.IMAGE,
195 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
196 + "text,\n" + "lineart.",
197 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
199 "Sets a scaling for bitmap image format (PNG). Should be given as a floating point number. If used in conjunction with --width and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
200 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
202 "Sets a width for bitmap image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --height then the smallest scaling will be used (scale, width and height provide bounds for the image).",
203 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
205 "Sets a height for bitmap image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --scale and --width then the smallest scaling will be used (scale, width and height provide bounds for the image).",
206 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
207 IMAGECOLOUR(Type.IMAGE, "imagecolor", // being a bit soft on the Americans!
208 "Applies the colour scheme to the open alignment window for this image, otherwise the value of "
209 + Arg.COLOUR.argString()
210 + " (or none) will apply. Valid values are the same as "
211 + Arg.COLOUR.argString() + ".",
212 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
213 BGCOLOUR(Type.IMAGE, "bgcolor", // being a bit soft on the Americans!
214 "Applies a background colour to the structure image. Valid values are named colours known to Java or RRGGBB 6 digit hex-string.",
215 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
217 STRUCTUREIMAGETYPE(Type.IMAGE,
218 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
219 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
220 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
221 STRUCTUREIMAGETEXTRENDERER(Type.IMAGE,
222 "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
223 + "text,\n" + "lineart.",
224 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
225 STRUCTUREIMAGESCALE(Type.IMAGE,
226 "Sets a scaling for bitmap structure image format (PNG). Should be given as a floating point number. If used in conjunction with --structureimagewidth and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
227 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
228 STRUCTUREIMAGEWIDTH(Type.IMAGE,
229 "Sets a width for bitmap structure image format (PNG) with the height maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimageheight then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
230 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
231 STRUCTUREIMAGEHEIGHT(Type.IMAGE,
232 "Sets a height for bitmap structure image format (PNG) with the width maintaining the aspect ratio. Should be given as a positive integer. If used in conjunction with --structureimagescale and --structureimagewidth then the smallest scaling will be used (structureimagescale, structureimagewidth and structureimageheight provide bounds for the structure image).",
233 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
237 "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"
238 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
239 + "stockholm (sto, stk),\n" + "pir (pir),\n"
240 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
241 + "pileup (pileup),\n" + "msf (msf),\n"
242 + "clustal (aln),\n" + "phylip (phy),\n"
243 + "jalview (jvp, jar).",
244 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWMULTIID,
245 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.STDOUT, Opt.PRIMARY),
247 "Sets the format for the preceding --output file. Valid formats are:\n"
248 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
249 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
250 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
251 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
253 "Process a groovy script in the file for the open alignment.",
254 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
257 "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.",
258 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
259 OVERWRITE(Type.OUTPUT,
260 "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.",
261 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
263 "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.",
264 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
266 "Automatically create directories when outputting a file to a new directory.",
267 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
269 // controlling flow of arguments
271 "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.",
272 Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION,
273 Opt.INCREMENTDEFAULTCOUNTER),
274 SUBSTITUTIONS(Type.FLOW,
275 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
276 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
277 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
278 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
279 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
280 + "{n} - the value of the index counter (starting at 0).\n"
281 + "{++n} - increase and substitute the value of the index counter,\n"
282 + "{} - the value of the current alignment window default index.",
283 true, Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
285 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
286 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
287 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
288 Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.GLOB,
289 Opt.ALLOWSUBSTITUTIONS),
290 NPP(Type.FLOW, "n++",
291 "Increase the index counter used in argument value substitutions.",
292 Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION),
294 "Apply the following output arguments to all sets of linked arguments.",
295 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
297 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
298 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
300 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
303 "Secret arg to not quit after --headless mode for tests",
304 Opt.UNARY, Opt.SECRET),
305 ALLSTRUCTURES(Type.FLOW,
306 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
307 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
310 TESTOUTPUT(Type.CONFIG,
311 "Allow specific stdout information. For testing purposes only.",
312 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
313 SETPROP(Type.CONFIG, "Set an individual Java System property.",
314 Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.SECRET), // not in use
317 "This argument does nothing on its own, but can be used with linkedIds.",
318 Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET),
320 // private options (inserted during arg processing)
321 SETARGFILE(Type.FLOW,
322 "Sets the current value of the argfilename. Inserted before argfilecontents.",
323 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTIVALUE, Opt.PRIVATE,
325 UNSETARGFILE(Type.FLOW,
326 "Unsets the current value of the argfilename. Inserted after argfile contents.",
327 Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.PRIVATE, Opt.NOACTION),
329 // these last two have no purpose in the normal Jalview application but are
330 // used by jalview.bin.Launcher to set memory settings. They are not used by
331 // argparser but are here for Usage statement reasons.
332 JVMMEMPC(Type.CONFIG,
333 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
334 + "The equals sign (\"=\") separator must be used with no spaces.",
335 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
336 JVMMEMMAX(Type.CONFIG,
337 "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"
338 + "The equals sign (\"=\") separator must be used with no spaces.",
339 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
343 public static enum Opt
346 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
347 * A default can be given with setOptions(bool, Opt....).
348 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
350 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
351 + ArgParser.NEGATESTRING + "..."),
354 * A STRING Arg will take a value either through --arg=value or --arg value.
356 STRING("expects a value"),
358 * A UNARY Arg is a boolean value, true if present, false if not.
359 * Like BOOLEAN but without the --noarg option.
363 * A MULTI Arg can be specified multiple times.
364 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
366 MULTIVALUE("can be specified multiple times"),
368 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
369 * If no linkedId is specified then the current default linkedId will be used.
371 LINKED("is linked to an alignment"),
373 * A NODUPLICATES Arg can only have one value (per linkedId).
374 * The first value will be used and subsequent values ignored with a warning.
376 NODUPLICATEVALUES("cannot have the same value more than once"),
378 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
379 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
380 * See jalview.bin.argparser.BootstrapArgs.
382 BOOTSTRAP("a configuration argument"),
384 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
385 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
386 * but if specified as --arg=filename* then the Java glob expansion method will be used
387 * (see FileUtils.getFilenamesFromGlob()).
388 * Note that this might be different from the shell expansion rules.
390 GLOB("can take multiple filenames with wildcards"),
392 * A NOACTION Arg does not perform a data task,
393 * usually used to control flow in ArgParser.parse(args).
397 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
398 * SubVals and values.
400 ALLOWSUBSTITUTIONS("values can use substitutions"),
402 * A PRIVATE Arg is used internally, and cannot be specified by the user.
406 * A SECRET Arg is used by development processes and although it can be set by the user,
407 * it is not displayed to the user.
411 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
413 ALLOWMULTIID("can be used with " + ArgParser.DOUBLEDASH + "all"),
415 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
416 * the defaultLinkedIdCounter is incremented *first*.
418 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
420 * An INPUT Arg counts as an input for REQUIREINPUT
424 * A REQUIREINPUT Arg can only be applied via --all if there is an input
425 * (i.e. --open or --append)
429 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
430 * --all --output={basename}.ext
432 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
434 * A STDOUT Arg can take an output filename that can be '-' to mean print to STDOUT.
436 STDOUT("allows the output filename '" + ArgParser.STDOUTFILENAME
437 + "' to mean output to STDOUT"),
439 * A STORED Arg resets and creates a new set of "opened" linkedIds
443 * A HELP Arg is a --help type arg
445 HELP("provides a help statement"),
447 * A PRIMARY Arg is the main Arg for its type
449 PRIMARY("is a primary argument for its type"),
451 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
455 * A FIRST arg gets moved to appear first in the usage statement (within type)
459 * A LAST arg gets moved to appear last in the usage statement (within type)
463 * After other args are checked, the following args can prefix a KEY=VALUE argument
465 PREFIXKEV("prefixes key=value"),
467 * do not lowercase the name when getting the arg name or arg string
473 private String description;
480 private Opt(String description)
482 this.description = description;
485 public String description()
492 public static enum Type
494 // Type restricts argument to certain usage output
496 CONFIG("arguments used to configure "
497 + ChannelProperties.getProperty("app_name") + " from startup"),
498 OPENING("arguments used to open and format alignments"),
499 STRUCTURE("arguments used to add and format 3D structure data"),
500 PROCESS("arguments used to process an alignment once opened"),
501 OUTPUT("arguments used to save data from a processed alignment"),
502 IMAGE("arguments used to export an image of an alignment or structure"),
503 // IMAGE("arguments used to export an image of an alignment"),
504 // STRUCTUREIMAGE("arguments used to export an image of an structure"),
505 FLOW("arguments that control processing of the other arguments"), //
506 ALL("all arguments"), // mostly just a place-holder for --help-all
507 NONE, // mostly a place-holder for --help
510 private String description;
517 private Type(String description)
519 this.description = description;
522 public String description()
528 private final String[] argNames;
530 private Opt[] argOptions;
532 private boolean defaultBoolValue;
534 private String description;
538 private Arg(Type type, String description, Opt... options)
540 this(type, null, description, false, options);
543 private Arg(Type type, String description, boolean defaultBoolean,
546 this(type, null, description, defaultBoolean, options);
549 private Arg(Type type, String alternativeName, String description,
552 this(type, alternativeName, description, false, options);
555 private Arg(Type type, String alternativeName, String description,
556 boolean defaultBoolean, Opt... options)
559 this.description = description;
560 this.defaultBoolValue = defaultBoolean;
561 this.setOptions(options);
562 this.argNames = alternativeName != null
564 { this.getName(), alternativeName }
569 public String argString()
571 return argString(false);
574 public String negateArgString()
576 return argString(true);
579 private String argString(boolean negate)
581 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
582 if (negate && hasOption(Opt.BOOLEAN))
583 sb.append(ArgParser.NEGATESTRING);
584 sb.append(getName());
585 return sb.toString();
588 public String toLongString()
590 StringBuilder sb = new StringBuilder();
591 sb.append(this.getClass().getName()).append('.').append(this.name());
593 if (getNames().length > 0)
595 sb.append(String.join("\", \"", getNames()));
596 if (getNames().length > 0)
599 sb.append("\nType: " + type.name());
600 sb.append("\nOpt: ");
601 // map List<Opt> to List<String> for the String.join
602 List<String> optList = Arrays.asList(argOptions).stream()
603 .map(opt -> opt.name()).collect(Collectors.toList());
604 sb.append(String.join(", ", optList));
606 return sb.toString();
609 public String[] getNames()
614 public String getName()
616 String name = hasOption(Opt.PRESERVECASE) ? this.name()
617 : this.name().toLowerCase(Locale.ROOT);
618 return name.replace('_', '-');
622 public final String toString()
627 public boolean hasOption(Opt o)
629 if (argOptions == null)
631 for (Opt option : argOptions)
639 public boolean hasAllOptions(Opt... opts)
643 if (!this.hasOption(o))
649 protected Opt[] getOptions()
654 protected void setOptions(Opt... options)
656 this.argOptions = options;
659 protected boolean getDefaultBoolValue()
661 return defaultBoolValue;
664 public Type getType()
669 protected String getDescription()
674 public static String booleanArgString(Arg a)
676 StringBuilder sb = new StringBuilder(a.argString());
677 if (a.hasOption(Opt.BOOLEAN))
680 sb.append(a.negateArgString());
682 return sb.toString();
685 public static final String usage()
690 public static final void appendUsageGeneral(StringBuilder sb,
693 for (Type t : EnumSet.allOf(Type.class))
695 if (t.description() != null)
697 StringBuilder argSb = new StringBuilder();
698 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
699 .append(t.name().toLowerCase(Locale.ROOT));
700 appendArgAndDescription(sb, argSb.toString(),
701 "Help for " + t.description(), null, maxArgLength);
702 sb.append(System.lineSeparator());
707 public static final String usage(List<Type> types)
709 StringBuilder sb = new StringBuilder();
711 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
712 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
713 + "] file(s)] [args]");
714 sb.append(System.lineSeparator());
715 sb.append(System.lineSeparator());
717 if (types == null || types.contains(null))
719 // always show --help
720 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
722 sb.append(System.lineSeparator());
724 appendUsageGeneral(sb, DESCRIPTIONINDENT);
728 List<Arg> args = argsSortedForDisplay(types);
731 * just use a set maxArgLength of DESCRIPTIONINDENT
733 int maxArgLength = 0;
736 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
739 String argS = argDisplayString(a);
740 if (argS.length() > maxArgLength)
741 maxArgLength = argS.length();
744 int maxArgLength = DESCRIPTIONINDENT;
746 // always show --help
747 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
748 sb.append(System.lineSeparator());
750 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
752 appendUsageGeneral(sb, maxArgLength);
755 Iterator<Arg> argsI = args.iterator();
756 Type typeSection = null;
757 while (argsI.hasNext())
759 Arg a = argsI.next();
761 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
767 if (a.getType() != typeSection)
769 typeSection = a.getType();
770 String typeDescription = a.getType().description();
771 if (typeDescription != null && typeDescription.length() > 0)
773 // typeDescription = typeDescription.substring(0,
774 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
775 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
776 sb.append(typeDescription);
777 sb.append(System.lineSeparator());
778 sb.append(System.lineSeparator());
782 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
786 sb.append(System.lineSeparator());
790 return sb.toString();
793 private static void appendArgUsage(StringBuilder sb, Arg a,
794 int maxArgLength, int maxWidth)
796 boolean first = appendArgAndDescription(sb, null, null, a,
798 List<String> options = new ArrayList<>();
800 for (Opt o : EnumSet.allOf(Opt.class))
802 if (a.hasOption(o) && o.description() != null)
804 options.add(o.description());
808 final String optDisplaySeparator = "; ";
809 if (options.size() > 0)
812 String spacing = String.format("%-"
813 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
817 sb.append(ARGDESCRIPTIONSEPARATOR);
818 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
823 linelength += spacing.length();
825 if (options.size() > 0)
827 boolean optFirst = true;
828 Iterator<String> optionsI = options.listIterator();
829 while (optionsI.hasNext())
831 String desc = optionsI.next();
837 int descLength = desc.length()
838 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
839 if (linelength + descLength > maxWidth)
841 sb.append(System.lineSeparator());
844 linelength += spacing.length();
846 // sb.append(linelength + "+" + desc.length() + " ");
848 linelength += desc.length();
849 if (optionsI.hasNext())
851 sb.append(optDisplaySeparator);
852 linelength += optDisplaySeparator.length();
857 sb.append(System.lineSeparator());
862 public static String argDisplayString(Arg a)
864 StringBuilder argSb = new StringBuilder();
866 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
867 if (a.hasOption(Opt.STRING))
869 if (a.hasOption(Opt.PREFIXKEV))
871 argSb.append("key=value");
875 argSb.append("=value");
878 return argSb.toString();
881 public static boolean appendArgAndDescription(StringBuilder sb,
882 String aString, String description, Arg a, int maxArgLength)
884 return appendArgAndDescription(sb, aString, description, a,
885 maxArgLength, Platform.consoleWidth());
888 public static boolean appendArgAndDescription(StringBuilder sb,
889 String aString, String description, Arg a, int maxArgLength,
892 if (aString == null && a != null)
894 aString = argDisplayString(a);
896 if (description == null && a != null)
898 description = a.getDescription();
900 sb.append(String.format("%-" + maxArgLength + "s", aString));
901 if (aString.length() > maxArgLength)
903 sb.append(System.lineSeparator());
904 sb.append(String.format("%-" + maxArgLength + "s", ""));
907 int descLength = maxLength - maxArgLength
908 - ARGDESCRIPTIONSEPARATOR.length();
909 // reformat the descriptions lines to the right width
910 Iterator<String> descLines = null;
911 if (description != null)
913 descLines = Arrays.stream(description.split("\\n")).iterator();
915 List<String> splitDescLinesList = new ArrayList<>();
916 while (descLines != null && descLines.hasNext())
918 String line = descLines.next();
919 while (line.length() > descLength)
921 int splitIndex = line.lastIndexOf(" ", descLength);
922 splitDescLinesList.add(line.substring(0, splitIndex));
923 line = line.substring(splitIndex + 1);
925 splitDescLinesList.add(line);
928 Iterator<String> splitDescLines = splitDescLinesList.iterator();
929 boolean first = true;
930 if (splitDescLines != null)
932 while (splitDescLines.hasNext())
936 sb.append(ARGDESCRIPTIONSEPARATOR);
940 sb.append(String.format("%-"
941 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
944 sb.append(splitDescLines.next());
945 sb.append(System.lineSeparator());
952 protected static Iterator<Arg> getAllOfType(Type type)
954 return getAllOfType(type, new Opt[] {});
957 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
959 Opt[] opts = options == null ? new Opt[] {} : options;
960 return EnumSet.allOf(Arg.class).stream().filter(a -> {
961 if (a.getType() != type)
972 private static List<Arg> argsSortedForDisplay(List<Type> types)
974 List<Arg> argsToSort;
975 // if no types provided, do all
976 if (types == null || types.size() == 0 || types.contains(Type.ALL))
979 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
983 argsToSort = new ArrayList<>();
984 for (Type type : types)
988 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
992 Collections.sort(argsToSort, new ArgDisplayComparator());
996 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
998 private static final int DESCRIPTIONINDENT = 20;
1002 class ArgDisplayComparator implements Comparator<Arg>
1004 private int compareArgOpts(Arg a, Arg b, Opt o)
1006 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
1007 : (b.hasOption(o) ? 1 : 0);
1011 private int compareForDisplay(Arg a, Arg b)
1015 // first compare types (in enum order)
1016 int i = a.getType().compareTo(b.getType());
1019 // do Opt.LAST next (oddly). Reversed args important!
1020 i = compareArgOpts(b, a, Opt.LAST);
1024 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
1026 for (Opt o : optOrder)
1028 i = compareArgOpts(a, b, o);
1032 // finally order of appearance in enum declarations
1033 return a.compareTo(b);
1037 public int compare(Arg a, Arg b)
1039 return compareForDisplay(a, b);