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.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.stream.Collectors;
35 import jalview.bin.argparser.Arg.Opt;
36 import jalview.util.ChannelProperties;
37 import jalview.util.Platform;
42 // Initialising arguments (BOOTSTRAP)
43 HELP(Type.HELP, "h", "Display basic help", Opt.UNARY, Opt.BOOTSTRAP,
44 Opt.HASTYPE, Opt.MULTIVALUE),
46 * Other --help-type Args will be added by the static block.
48 VERSION(Type.CONFIG, "v",
49 "Display the version of "
50 + ChannelProperties.getProperty("app_name"),
51 Opt.UNARY, Opt.BOOTSTRAP),
53 "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed. "
54 + "Headless mode is assumed if an output file is to be generated, this can be overridden with --gui.",
55 Opt.UNARY, Opt.BOOTSTRAP),
57 "Do not run Jalview in headless mode. This overrides the assumption of headless mode when an output file is to be generated.",
58 Opt.UNARY, Opt.BOOTSTRAP),
59 JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
60 Opt.STRING, Opt.BOOTSTRAP),
61 NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
62 Opt.BOOLEAN, Opt.BOOTSTRAP),
64 "Show (or don't show) the About Jalview splash screen.", true,
65 Opt.BOOLEAN, Opt.BOOTSTRAP),
66 QUESTIONNAIRE(Type.CONFIG,
67 "Show (or don't show) the questionnaire if one is available.",
68 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
69 JAVACONSOLE(Type.CONFIG, "Show (or don't show) the Java Console.", false,
70 Opt.BOOLEAN, Opt.BOOTSTRAP),
71 NOUSAGESTATS(Type.CONFIG, "Don't send initial launch usage stats.",
72 Opt.UNARY, Opt.BOOTSTRAP),
73 NOSTARTUPFILE(Type.CONFIG, "Don't show the default startup file.",
74 Opt.UNARY, Opt.BOOTSTRAP),
75 WEBSERVICEDISCOVERY(Type.CONFIG,
76 "Attempt (or don't attempt) to connect to JABAWS web services.",
77 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
79 "Use a file as the preferences file instead of the usual ~/"
80 + ChannelProperties.getProperty("preferences.filename")
82 Opt.STRING, Opt.BOOTSTRAP),
83 DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
85 TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
86 Opt.BOOTSTRAP, Opt.SECRET),
87 QUIET(Type.CONFIG, "q",
88 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
89 Opt.UNARY, Opt.MULTIVALUE, Opt.BOOTSTRAP),
90 INITSUBSTITUTIONS(Type.CONFIG,
91 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
92 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
93 P(Type.CONFIG, "Set a Jalview preference value for this session.",
94 Opt.PREFIXKEV, Opt.PRESERVECASE, Opt.STRING, Opt.BOOTSTRAP,
95 Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET), // keep this secret for
98 // Opening an alignment
100 "Opens one or more alignment files or URLs in new alignment windows.",
101 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER,
102 Opt.MULTIVALUE, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT,
103 Opt.STORED, Opt.PRIMARY),
105 "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
106 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.GLOB,
107 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
109 "Specifies the title for the open alignment window as string.",
110 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
111 COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
112 "Applies the colour scheme to the open alignment window. Valid values include:\n"
113 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
114 + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
115 + "gecos-blossom,\n" + "gecos-sunset,\n"
116 + "gecos-ocean,\n" + "hydrophobic,\n"
117 + "helix-propensity,\n" + "strand-propensity,\n"
118 + "turn-propensity,\n" + "buried-index,\n"
119 + "nucleotide,\n" + "nucleotide-ambiguity,\n"
120 + "purine-pyrimidine,\n" + "rna-helices,\n"
121 + "t-coffee-scores,\n" + "sequence-id.\n" + "\n"
122 + "Names of user defined colourschemes will also work,\n"
123 + "and jalview colourscheme specifications like\n"
124 + "--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
125 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
126 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
127 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
129 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
130 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
132 SORTBYTREE(Type.OPENING,
133 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
134 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWMULTIID),
135 ANNOTATIONS(Type.OPENING,
136 "Add an annotations file or URL to the open alignment.",
137 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
139 SHOWANNOTATIONS(Type.OPENING,
140 "Enforces showing (or not showing) alignment annotations.",
141 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
143 "Enforces wrapped (or not wrapped) alignment formatting.",
144 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
145 NOSTRUCTURE(Type.OPENING,
146 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
147 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
149 // Adding a 3D structure
150 STRUCTURE(Type.STRUCTURE,
151 "Load a structure file or URL associated with a sequence in the open alignment.\n"
152 + "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.",
153 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
154 Opt.PRIMARY, Opt.ALLOWMULTIID),
155 SEQID(Type.STRUCTURE,
156 "Specify the sequence name for the preceding --structure to be associated with.",
157 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
159 PAEMATRIX(Type.STRUCTURE,
160 "Add a PAE json matrix file to the preceding --structure.",
161 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
163 TEMPFAC(Type.STRUCTURE,
164 "Set the type of temperature factor. Possible values are:\n"
165 + "default,\n" + "plddt.",
166 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
167 STRUCTUREVIEWER(Type.STRUCTURE,
168 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
169 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
171 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
172 NOTEMPFAC(Type.STRUCTURE,
173 "Do not show the temperature factor annotation for the preceding --structure.",
174 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.SECRET), // keep this
177 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
182 "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"
183 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
184 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.MULTIVALUE,
185 Opt.ALLOWMULTIID, Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
186 STRUCTUREIMAGE(new Type[]
187 { Type.IMAGE, Type.STRUCTUREIMAGE },
188 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
189 Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID,
192 "Set the image format for the preceding " + Arg.IMAGE.argString()
193 + " or " + Arg.STRUCTUREIMAGE.argString()
194 + ". Valid values are:\n" + "svg,\n" + "png,\n" + "eps,\n"
195 + "html,\n" + "biojs.",
196 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
197 TEXTRENDERER(Type.IMAGE,
198 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
199 + "text,\n" + "lineart.",
200 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
202 "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).",
203 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
205 "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).",
206 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
208 "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).",
209 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
210 IMAGECOLOUR(Type.IMAGE, "imagecolor", // being a bit soft on the Americans!
211 "Applies the colour scheme to the open alignment window for this image, otherwise the value of "
212 + Arg.COLOUR.argString()
213 + " (or none) will apply. Valid values are the same as "
214 + Arg.COLOUR.argString() + ".",
215 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
216 BGCOLOUR(Type.IMAGE, "bgcolor", // being a bit soft on the Americans!
217 "Applies a background colour to the structure image. Valid values are named colours known to Java or RRGGBB 6 digit hex-string.",
218 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
220 "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"
221 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
222 + "stockholm (sto, stk),\n" + "pir (pir),\n"
223 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
224 + "pileup (pileup),\n" + "msf (msf),\n"
225 + "clustal (aln),\n" + "phylip (phy),\n"
226 + "jalview (jvp, jar).",
227 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWMULTIID,
228 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.STDOUT, Opt.PRIMARY),
230 "Sets the format for the preceding --output file. Valid formats are:\n"
231 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
232 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
233 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
234 Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
236 "Process a groovy script in the file for the open alignment.",
237 Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
240 "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.",
241 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
242 OVERWRITE(Type.OUTPUT,
243 "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.",
244 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
246 "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.",
247 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
249 "Automatically create directories when outputting a file to a new directory.",
250 Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
252 // controlling flow of arguments
254 "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.",
255 Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION,
256 Opt.INCREMENTDEFAULTCOUNTER),
257 SUBSTITUTIONS(Type.FLOW,
258 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
259 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
260 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
261 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
262 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
263 + "{n} - the value of the index counter (starting at 0).\n"
264 + "{++n} - increase and substitute the value of the index counter,\n"
265 + "{} - the value of the current alignment window default index.",
266 true, Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
268 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
269 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
270 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
271 Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.GLOB,
272 Opt.ALLOWSUBSTITUTIONS),
273 NPP(Type.FLOW, "n++",
274 "Increase the index counter used in argument value substitutions.",
275 Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION),
277 "Apply the following output arguments to all sets of linked arguments.",
278 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
280 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
281 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
283 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
286 "Secret arg to not quit after --headless mode for tests",
287 Opt.UNARY, Opt.SECRET),
288 ALLSTRUCTURES(Type.FLOW,
289 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
290 Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
293 TESTOUTPUT(Type.CONFIG,
294 "Allow specific stdout information. For testing purposes only.",
295 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
296 SETPROP(Type.CONFIG, "Set an individual Java System property.",
297 Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.SECRET), // not in use
300 "This argument does nothing on its own, but can be used with linkedIds.",
301 Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET),
303 // private options (inserted during arg processing)
304 SETARGFILE(Type.FLOW,
305 "Sets the current value of the argfilename. Inserted before argfilecontents.",
306 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTIVALUE, Opt.PRIVATE,
308 UNSETARGFILE(Type.FLOW,
309 "Unsets the current value of the argfilename. Inserted after argfile contents.",
310 Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.PRIVATE, Opt.NOACTION),
312 // these last two have no purpose in the normal Jalview application but are
313 // used by jalview.bin.Launcher to set memory settings. They are not used by
314 // argparser but are here for Usage statement and parsing reasons.
315 JVMMEMPC(Type.CONFIG,
316 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
317 + "The equals sign (\"=\") separator must be used with no spaces.",
318 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
319 JVMMEMMAX(Type.CONFIG,
320 "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"
321 + "The equals sign (\"=\") separator must be used with no spaces.",
322 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
326 public static enum Opt
329 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
330 * A default can be given with setOptions(bool, Opt....).
331 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
333 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
334 + ArgParser.NEGATESTRING + "..."),
337 * A STRING Arg will take a value either through --arg=value or --arg value.
339 STRING("expects a value"),
341 * A UNARY Arg is a boolean value, true if present, false if not.
342 * Like BOOLEAN but without the --noarg option.
346 * A MULTI Arg can be specified multiple times.
347 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
349 MULTIVALUE("can be specified multiple times"),
351 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
352 * If no linkedId is specified then the current default linkedId will be used.
354 LINKED("is linked to an alignment"),
356 * A NODUPLICATES Arg can only have one value (per linkedId).
357 * The first value will be used and subsequent values ignored with a warning.
359 NODUPLICATEVALUES("cannot have the same value more than once"),
361 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
362 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
363 * See jalview.bin.argparser.BootstrapArgs.
365 BOOTSTRAP("a configuration argument"),
367 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
368 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
369 * but if specified as --arg=filename* then the Java glob expansion method will be used
370 * (see FileUtils.getFilenamesFromGlob()).
371 * Note that this might be different from the shell expansion rules.
373 GLOB("can take multiple filenames with wildcards"),
375 * A NOACTION Arg does not perform a data task,
376 * usually used to control flow in ArgParser.parse(args).
380 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
381 * SubVals and values.
383 ALLOWSUBSTITUTIONS("values can use substitutions"),
385 * A PRIVATE Arg is used internally, and cannot be specified by the user.
389 * A SECRET Arg is used by development processes and although it can be set by the user,
390 * it is not displayed to the user.
394 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
396 ALLOWMULTIID("can be used with " + ArgParser.DOUBLEDASH + "all"),
398 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
399 * the defaultLinkedIdCounter is incremented *first*.
401 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
403 * An INPUT Arg counts as an input for REQUIREINPUT
407 * A REQUIREINPUT Arg can only be applied via --all if there is an input
408 * (i.e. --open or --append)
412 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
413 * --all --output={basename}.ext
415 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
417 * A STDOUT Arg can take an output filename that can be '-' to mean print to STDOUT.
419 STDOUT("allows the output filename '" + ArgParser.STDOUTFILENAME
420 + "' to mean output to STDOUT"),
422 * A STORED Arg resets and creates a new set of "opened" linkedIds
426 * A HELP Arg is a --help type arg
428 HELP("provides a help statement"),
430 * A PRIMARY Arg is the main Arg for its type
432 PRIMARY("is a primary argument for its type"),
434 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
438 * A FIRST arg gets moved to appear first in the usage statement (within type)
442 * A LAST arg gets moved to appear last in the usage statement (within type)
446 * After other args are checked, the following args can prefix a KEY=VALUE argument
448 PREFIXKEV("prefixes key=value"),
450 * do not lowercase the name when getting the arg name or arg string
456 private String description;
463 private Opt(String description)
465 this.description = description;
468 public String description()
475 public static enum Type
477 // Type restricts argument to certain usage output
479 CONFIG("arguments used to configure "
480 + ChannelProperties.getProperty("app_name") + " from startup"),
481 OPENING("arguments used to open and format alignments"),
482 STRUCTURE("arguments used to add and format 3D structure data"),
483 PROCESS("arguments used to process an alignment once opened"),
484 OUTPUT("arguments used to save data from a processed alignment"),
485 IMAGE("arguments used to export an image of an alignment"),
486 STRUCTUREIMAGE("arguments used to export an image of an structure"),
487 FLOW("arguments that control processing of the other arguments"), //
488 ALL("all arguments"), // mostly just a place-holder for --help-all
489 NONE, // mostly a place-holder for --help
492 private String description;
499 private Type(String description)
501 this.description = description;
504 public String description()
510 private final String[] argNames;
512 private Opt[] argOptions;
514 private boolean defaultBoolValue;
516 private String description;
518 private Type[] types;
520 private Arg(Type type, String description, Opt... options)
522 this(new Type[] { type }, description, options);
525 private Arg(Type[] type, String description, Opt... options)
527 this(type, null, description, false, options);
530 private Arg(Type type, String description, boolean defaultBoolean,
533 this(new Type[] { type }, description, defaultBoolean, options);
536 private Arg(Type[] type, String description, boolean defaultBoolean,
539 this(type, null, description, defaultBoolean, options);
542 private Arg(Type type, String alternativeName, String description,
545 this(new Type[] { type }, alternativeName, description, options);
548 private Arg(Type[] type, String alternativeName, String description,
551 this(type, alternativeName, description, false, options);
554 private Arg(Type type, String alternativeName, String description,
555 boolean defaultBoolean, Opt... options)
557 this(new Type[] { type }, alternativeName, description, defaultBoolean,
561 private Arg(Type[] type, String alternativeName, String description,
562 boolean defaultBoolean, Opt... options)
565 this.description = description;
566 this.defaultBoolValue = defaultBoolean;
567 this.setOptions(options);
568 this.argNames = alternativeName != null
570 { this.getName(), alternativeName }
575 public String argString()
577 return argString(false);
580 public String negateArgString()
582 return argString(true);
585 private String argString(boolean negate)
587 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
588 if (negate && hasOption(Opt.BOOLEAN))
589 sb.append(ArgParser.NEGATESTRING);
590 sb.append(getName());
591 return sb.toString();
594 public String toLongString()
596 StringBuilder sb = new StringBuilder();
597 sb.append(this.getClass().getName()).append('.').append(this.name());
599 if (getNames().length > 0)
601 sb.append(String.join("\", \"", getNames()));
602 if (getNames().length > 0)
605 for (Type type : getTypes())
607 String typeName = type.name();
608 sb.append("\nType: " + typeName);
610 sb.append("\nOpt: ");
611 // map List<Opt> to List<String> for the String.join
612 List<String> optList = Arrays.asList(argOptions).stream()
613 .map(opt -> opt.name()).collect(Collectors.toList());
614 sb.append(String.join(", ", optList));
616 return sb.toString();
619 public String[] getNames()
624 public String getName()
626 String name = hasOption(Opt.PRESERVECASE) ? this.name()
627 : this.name().toLowerCase(Locale.ROOT);
628 return name.replace('_', '-');
632 public final String toString()
637 public boolean hasOption(Opt o)
639 if (argOptions == null)
641 for (Opt option : argOptions)
649 public boolean hasAllOptions(Opt... opts)
653 if (!this.hasOption(o))
659 protected Opt[] getOptions()
664 protected void setOptions(Opt... options)
666 this.argOptions = options;
669 protected boolean getDefaultBoolValue()
671 return defaultBoolValue;
674 public Type getFirstType()
676 return this.getTypes()[0];
679 public Type[] getTypes()
684 public boolean sharesType(Arg a)
686 return this.hasType(a.getTypes());
689 public boolean hasType(Type... types)
691 Set<Type> typesSet = new HashSet<>(Arrays.asList(types));
692 return this.hasType(typesSet);
695 public boolean hasType(Set<Type> typesSet)
697 for (Type type : getTypes())
699 if (typesSet.contains(type))
707 protected String getDescription()
712 public static String booleanArgString(Arg a)
714 StringBuilder sb = new StringBuilder(a.argString());
715 if (a.hasOption(Opt.BOOLEAN))
718 sb.append(a.negateArgString());
720 return sb.toString();
723 public static final String usage()
728 public static final void appendUsageGeneral(StringBuilder sb,
731 Set<Type> firstTypes = new HashSet<>();
732 for (Arg a : EnumSet.allOf(Arg.class))
734 if (!firstTypes.contains(a.getFirstType()))
736 firstTypes.add(a.getFirstType());
739 for (Type t : EnumSet.allOf(Type.class))
741 if (t.description() != null && firstTypes.contains(t))
743 StringBuilder argSb = new StringBuilder();
744 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
745 .append(t.name().toLowerCase(Locale.ROOT));
746 appendArgAndDescription(sb, argSb.toString(),
747 "Help for " + t.description(), null, maxArgLength);
748 sb.append(System.lineSeparator());
753 public static final String usage(List<Type> types)
755 StringBuilder sb = new StringBuilder();
757 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
758 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
759 + "] file(s)] [args]");
760 sb.append(System.lineSeparator());
761 sb.append(System.lineSeparator());
763 if (types == null || types.contains(null))
765 // always show --help
766 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
768 sb.append(System.lineSeparator());
770 appendUsageGeneral(sb, DESCRIPTIONINDENT);
774 List<Arg> args = argsSortedForDisplay(types);
776 int maxArgLength = DESCRIPTIONINDENT;
778 // always show --help
779 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
780 sb.append(System.lineSeparator());
782 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
784 appendUsageGeneral(sb, maxArgLength);
787 Iterator<Arg> argsI = args.iterator();
788 Type typeSection = null;
789 while (argsI.hasNext())
791 Arg a = argsI.next();
793 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
799 if (a.getFirstType() != typeSection)
801 typeSection = a.getFirstType();
802 String typeDescription = a.getFirstType().description();
803 if (typeDescription != null && typeDescription.length() > 0)
805 // typeDescription = typeDescription.substring(0,
806 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
807 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
808 sb.append(typeDescription);
809 sb.append(System.lineSeparator());
810 sb.append(System.lineSeparator());
814 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
818 sb.append(System.lineSeparator());
822 return sb.toString();
825 private static void appendArgUsage(StringBuilder sb, Arg a,
826 int maxArgLength, int maxWidth)
828 boolean first = appendArgAndDescription(sb, null, null, a,
830 List<String> options = new ArrayList<>();
832 for (Opt o : EnumSet.allOf(Opt.class))
834 if (a.hasOption(o) && o.description() != null)
836 options.add(o.description());
840 final String optDisplaySeparator = "; ";
841 if (options.size() > 0)
844 String spacing = String.format("%-"
845 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
849 sb.append(ARGDESCRIPTIONSEPARATOR);
850 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
855 linelength += spacing.length();
857 if (options.size() > 0)
859 boolean optFirst = true;
860 Iterator<String> optionsI = options.listIterator();
861 while (optionsI.hasNext())
863 String desc = optionsI.next();
869 int descLength = desc.length()
870 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
871 if (linelength + descLength > maxWidth)
873 sb.append(System.lineSeparator());
876 linelength += spacing.length();
878 // sb.append(linelength + "+" + desc.length() + " ");
880 linelength += desc.length();
881 if (optionsI.hasNext())
883 sb.append(optDisplaySeparator);
884 linelength += optDisplaySeparator.length();
889 sb.append(System.lineSeparator());
894 public static String argDisplayString(Arg a)
896 StringBuilder argSb = new StringBuilder();
898 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
899 if (a.hasOption(Opt.STRING))
901 if (a.hasOption(Opt.PREFIXKEV))
903 argSb.append("key=value");
907 argSb.append("=value");
910 return argSb.toString();
913 public static boolean appendArgAndDescription(StringBuilder sb,
914 String aString, String description, Arg a, int maxArgLength)
916 return appendArgAndDescription(sb, aString, description, a,
917 maxArgLength, Platform.consoleWidth());
920 public static boolean appendArgAndDescription(StringBuilder sb,
921 String aString, String description, Arg a, int maxArgLength,
924 if (aString == null && a != null)
926 aString = argDisplayString(a);
928 if (description == null && a != null)
930 description = a.getDescription();
932 sb.append(String.format("%-" + maxArgLength + "s", aString));
933 if (aString.length() > maxArgLength)
935 sb.append(System.lineSeparator());
936 sb.append(String.format("%-" + maxArgLength + "s", ""));
939 int descLength = maxLength - maxArgLength
940 - ARGDESCRIPTIONSEPARATOR.length();
941 // reformat the descriptions lines to the right width
942 Iterator<String> descLines = null;
943 if (description != null)
945 descLines = Arrays.stream(description.split("\\n")).iterator();
947 List<String> splitDescLinesList = new ArrayList<>();
948 while (descLines != null && descLines.hasNext())
950 String line = descLines.next();
951 while (line.length() > descLength)
953 int splitIndex = line.lastIndexOf(" ", descLength);
954 splitDescLinesList.add(line.substring(0, splitIndex));
955 line = line.substring(splitIndex + 1);
957 splitDescLinesList.add(line);
960 Iterator<String> splitDescLines = splitDescLinesList.iterator();
961 boolean first = true;
962 if (splitDescLines != null)
964 while (splitDescLines.hasNext())
968 sb.append(ARGDESCRIPTIONSEPARATOR);
972 sb.append(String.format("%-"
973 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
976 sb.append(splitDescLines.next());
977 sb.append(System.lineSeparator());
984 protected static Iterator<Arg> getAllOfType(Type type)
986 return getAllOfType(type, new Opt[] {});
989 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
991 Opt[] opts = options == null ? new Opt[] {} : options;
992 return EnumSet.allOf(Arg.class).stream().filter(a -> {
993 if (!a.hasType(type))
1004 private static List<Arg> argsSortedForDisplay(List<Type> types)
1006 List<Arg> argsToSort;
1007 // if no types provided, do all
1008 if (types == null || types.size() == 0 || types.contains(Type.ALL))
1011 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
1015 argsToSort = new ArrayList<>();
1016 for (Type type : types)
1020 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
1024 Collections.sort(argsToSort, new ArgDisplayComparator());
1028 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
1030 private static final int DESCRIPTIONINDENT = 20;
1034 class ArgDisplayComparator implements Comparator<Arg>
1036 private int compareArgOpts(Arg a, Arg b, Opt o)
1038 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
1039 : (b.hasOption(o) ? 1 : 0);
1043 private int compareForDisplay(Arg a, Arg b)
1047 // first compare types (in enum order)
1048 int i = a.getFirstType().compareTo(b.getFirstType());
1051 // do Opt.LAST next (oddly). Reversed args important!
1052 i = compareArgOpts(b, a, Opt.LAST);
1056 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
1058 for (Opt o : optOrder)
1060 i = compareArgOpts(a, b, o);
1064 // finally order of appearance in enum declarations
1065 return a.compareTo(b);
1069 public int compare(Arg a, Arg b)
1071 return compareForDisplay(a, b);