1 package jalview.bin.argparser;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.EnumSet;
8 import java.util.Iterator;
10 import java.util.Locale;
11 import java.util.stream.Collectors;
13 import jalview.bin.argparser.Arg.Opt;
14 import jalview.util.ChannelProperties;
15 import jalview.util.Platform;
20 // Initialising arguments (BOOTSTRAP)
21 HELP(Type.HELP, "h", "Display basic help", Opt.UNARY, Opt.BOOTSTRAP,
22 Opt.HASTYPE, Opt.MULTI),
24 * Other --help-type Args will be added by the static block.
26 VERSION(Type.CONFIG, "v",
27 "Display the version of "
28 + ChannelProperties.getProperty("app_name"),
29 Opt.UNARY, Opt.BOOTSTRAP),
31 "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed. "
32 + "Headless mode is assumed if an output file is to be generated, this can be overridden with --gui.",
33 Opt.UNARY, Opt.BOOTSTRAP),
35 "Do not run Jalview in headless mode. This overrides the assumption of headless mode when an output file is to be generated.",
36 Opt.UNARY, Opt.BOOTSTRAP),
37 JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
38 Opt.STRING, Opt.BOOTSTRAP),
39 NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
40 Opt.BOOLEAN, Opt.BOOTSTRAP),
42 "Show (or don't show) the About Jalview splash screen.", true,
43 Opt.BOOLEAN, Opt.BOOTSTRAP),
44 QUESTIONNAIRE(Type.CONFIG,
45 "Show (or don't show) the questionnaire if one is available.",
46 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
47 NOUSAGESTATS(Type.CONFIG, "Don't send initial launch usage stats.",
48 Opt.UNARY, Opt.BOOTSTRAP),
49 NOSTARTUPFILE(Type.CONFIG, "Don't show the default startup file.",
50 Opt.UNARY, Opt.BOOTSTRAP),
51 WEBSERVICEDISCOVERY(Type.CONFIG,
52 "Attempt (or don't attempt) to connect to JABAWS web services.",
53 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
55 "Use a file as the preferences file instead of the usual ~/"
56 + ChannelProperties.getProperty("preferences.filename")
58 Opt.STRING, Opt.BOOTSTRAP),
59 DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
61 TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
62 Opt.BOOTSTRAP, Opt.SECRET),
63 QUIET(Type.CONFIG, "q",
64 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
65 Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
66 INITSUBSTITUTIONS(Type.CONFIG,
67 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
68 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
69 P(Type.CONFIG, "Set a Jalview preference value for this session.",
70 Opt.PREFIXKEV, Opt.PRESERVECASE, Opt.STRING, Opt.BOOTSTRAP,
71 Opt.MULTI, Opt.NOACTION, Opt.SECRET), // keep this secret for now.
73 // Opening an alignment
75 "Opens one or more alignment files or URLs in new alignment windows.",
76 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
77 Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.STORED,
80 "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
81 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
82 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
84 "Specifies the title for the open alignment window as string.",
85 Opt.STRING, Opt.LINKED),
86 COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
87 "Applies the colour scheme to the open alignment window. Valid values include:\n"
88 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
89 + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
90 + "gecos-blossom,\n" + "gecos-sunset,\n"
91 + "gecos-ocean,\n" + "hydrophobic,\n"
92 + "helix-propensity,\n" + "strand-propensity,\n"
93 + "turn-propensity,\n" + "buried-index,\n"
94 + "nucleotide,\n" + "nucleotide-ambiguity,\n"
95 + "purine-pyrimidine,\n" + "rna-helices,\n"
96 + "t-coffee-scores,\n" + "sequence-id.\n" + "\n"
97 + "Names of user defined colourschemes will also work,\n"
98 + "and jalview colourscheme specifications like\n"
99 + "--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
100 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
101 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
102 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
103 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
104 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
105 SORTBYTREE(Type.OPENING,
106 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
107 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
108 ANNOTATIONS(Type.OPENING,
109 "Add an annotations file or URL to the open alignment.",
110 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
111 SHOWANNOTATIONS(Type.OPENING,
112 "Enforces showing (or not showing) alignment annotations.",
113 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
115 "Enforces wrapped (or not wrapped) alignment formatting.",
116 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
117 NOSTRUCTURE(Type.OPENING,
118 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
119 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
121 // Adding a 3D structure
122 STRUCTURE(Type.STRUCTURE,
123 "Load a structure file or URL associated with a sequence in the open alignment.\n"
124 + "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.",
125 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
127 SEQID(Type.STRUCTURE,
128 "Specify the sequence name for the preceding --structure to be associated with.",
129 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
130 PAEMATRIX(Type.STRUCTURE,
131 "Add a PAE json matrix file to the preceding --structure.",
132 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
133 TEMPFAC(Type.STRUCTURE,
134 "Set the type of temperature factor. Possible values are:\n"
135 + "default,\n" + "plddt.",
136 Opt.STRING, Opt.LINKED),
137 STRUCTUREVIEWER(Type.STRUCTURE,
138 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
139 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
141 Opt.STRING, Opt.LINKED, Opt.MULTI),
142 NOTEMPFAC(Type.STRUCTURE,
143 "Do not show the temperature factor annotation for the preceding --structure.",
144 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
146 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
151 "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"
152 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
153 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
154 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
156 "Set the image format for the preceding --image. Valid values are:\n"
157 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
158 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
159 TEXTRENDERER(Type.IMAGE,
160 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
161 + "text,\n" + "lineart.",
162 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
164 "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).",
165 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
167 "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).",
168 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
170 "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).",
171 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
172 STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
173 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
174 Opt.LINKED, Opt.MULTI, Opt.OUTPUTFILE),
175 STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
176 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
177 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
178 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
179 STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
180 "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
181 + "text,\n" + "lineart.",
182 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
183 STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
184 "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).",
185 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
186 STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
187 "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).",
188 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
189 STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
190 "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).",
191 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
194 "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"
195 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
196 + "stockholm (sto, stk),\n" + "pir (pir),\n"
197 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
198 + "pileup (pileup),\n" + "msf (msf),\n"
199 + "clustal (aln),\n" + "phylip (phy),\n"
200 + "jalview (jvp, jar).",
201 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
202 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.STDOUT, Opt.PRIMARY),
204 "Sets the format for the preceding --output file. Valid formats are:\n"
205 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
206 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
207 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
208 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
210 "Process a groovy script in the file for the open alignment.",
211 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
214 "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.",
215 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
216 OVERWRITE(Type.OUTPUT,
217 "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.",
218 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
220 "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.",
221 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
223 // controlling flow of arguments
225 "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.",
226 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
227 SUBSTITUTIONS(Type.FLOW,
228 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
229 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
230 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
231 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
232 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
233 + "{n} - the value of the index counter (starting at 0).\n"
234 + "{++n} - increase and substitute the value of the index counter,\n"
235 + "{} - the value of the current alignment window default index.",
236 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
238 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
239 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
240 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
241 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
242 Opt.ALLOWSUBSTITUTIONS),
243 NPP(Type.FLOW, "n++",
244 "Increase the index counter used in argument value substitutions.",
245 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
247 "Apply the following output arguments to all sets of linked arguments.",
248 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
250 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
251 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
253 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
256 "Secret arg to not quit after --headless mode for tests",
257 Opt.UNARY, Opt.SECRET),
258 ALLSTRUCTURES(Type.FLOW,
259 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
260 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
263 TESTOUTPUT(Type.CONFIG,
264 "Allow specific stdout information. For testing purposes only.",
265 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
266 SETPROP(Type.CONFIG, "Set an individual Java System property.",
267 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
269 "This argument does nothing on its own, but can be used with linkedIds.",
270 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
272 // private options (inserted during arg processing)
273 SETARGFILE(Type.FLOW,
274 "Sets the current value of the argfilename. Inserted before argfilecontents.",
275 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
277 UNSETARGFILE(Type.FLOW,
278 "Unsets the current value of the argfilename. Inserted after argfile contents.",
279 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
281 // these last two have no purpose in the normal Jalview application but are
282 // used by jalview.bin.Launcher to set memory settings. They are not used by
283 // argparser but are here for Usage statement reasons.
284 JVMMEMPC(Type.CONFIG,
285 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
286 + "The equals sign (\"=\") separator must be used with no spaces.",
287 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
288 JVMMEMMAX(Type.CONFIG,
289 "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"
290 + "The equals sign (\"=\") separator must be used with no spaces.",
291 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
295 public static enum Opt
298 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
299 * A default can be given with setOptions(bool, Opt....).
300 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
302 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
303 + ArgParser.NEGATESTRING + "..."),
306 * A STRING Arg will take a value either through --arg=value or --arg value.
308 STRING("expects a value"),
310 * A UNARY Arg is a boolean value, true if present, false if not.
311 * Like BOOLEAN but without the --noarg option.
315 * A MULTI Arg can be specified multiple times.
316 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
318 MULTI("can be specified multiple times"),
320 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
321 * If no linkedId is specified then the current default linkedId will be used.
323 LINKED("is linked to an alignment"),
325 * A NODUPLICATES Arg can only have one value (per linkedId).
326 * The first value will be used and subsequent values ignored with a warning.
328 NODUPLICATEVALUES("cannot have the same value more than once"),
330 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
331 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
332 * See jalview.bin.argparser.BootstrapArgs.
334 BOOTSTRAP("a configuration argument"),
336 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
337 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
338 * but if specified as --arg=filename* then the Java glob expansion method will be used
339 * (see FileUtils.getFilenamesFromGlob()).
340 * Note that this might be different from the shell expansion rules.
342 GLOB("can take multiple filenames with wildcards"),
344 * A NOACTION Arg does not perform a data task,
345 * usually used to control flow in ArgParser.parse(args).
349 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
350 * SubVals and values.
352 ALLOWSUBSTITUTIONS("values can use substitutions"),
354 * A PRIVATE Arg is used internally, and cannot be specified by the user.
358 * A SECRET Arg is used by development processes and although it can be set by the user,
359 * it is not displayed to the user.
363 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
365 ALLOWALL("can be used with " + ArgParser.DOUBLEDASH + "all"),
367 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
368 * the defaultLinkedIdCounter is incremented *first*.
370 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
372 * An INPUT Arg counts as an input for REQUIREINPUT
376 * A REQUIREINPUT Arg can only be applied via --all if there is an input
377 * (i.e. --open or --append)
381 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
382 * --all --output={basename}.ext
384 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
386 * A STDOUT Arg can take an output filename that can be '-' to mean print to STDOUT.
388 STDOUT("allows the output filename '" + ArgParser.STDOUTFILENAME
389 + "' to mean output to STDOUT"),
391 * A STORED Arg resets and creates a new set of "opened" linkedIds
395 * A HELP Arg is a --help type arg
397 HELP("provides a help statement"),
399 * A PRIMARY Arg is the main Arg for its type
401 PRIMARY("is a primary argument for its type"),
403 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
407 * A FIRST arg gets moved to appear first in the usage statement (within type)
411 * A LAST arg gets moved to appear last in the usage statement (within type)
415 * After other args are checked, the following args can prefix a KEY=VALUE argument
417 PREFIXKEV("prefixes key=value"),
419 * do not lowercase the name when getting the arg name or arg string
425 private String description;
432 private Opt(String description)
434 this.description = description;
437 public String description()
444 public static enum Type
446 // Type restricts argument to certain usage output
448 CONFIG("arguments used to configure "
449 + ChannelProperties.getProperty("app_name") + " from startup"),
450 OPENING("arguments used to open and format alignments"),
451 STRUCTURE("arguments used to add and format 3D structure data"),
452 PROCESS("arguments used to process an alignment once opened"),
453 OUTPUT("arguments used to save data from a processed alignment"),
454 IMAGE("arguments used to export an image of an alignment"),
455 STRUCTUREIMAGE("arguments used to export an image of an structure"),
456 FLOW("arguments that control processing of the other arguments"), //
457 ALL("all arguments"), // mostly just a place-holder for --help-all
458 NONE, // mostly a place-holder for --help
461 private String description;
468 private Type(String description)
470 this.description = description;
473 public String description()
479 private final String[] argNames;
481 private Opt[] argOptions;
483 private boolean defaultBoolValue;
485 private String description;
489 private Arg(Type type, String description, Opt... options)
491 this(type, null, description, false, options);
494 private Arg(Type type, String description, boolean defaultBoolean,
497 this(type, null, description, defaultBoolean, options);
500 private Arg(Type type, String alternativeName, String description,
503 this(type, alternativeName, description, false, options);
506 private Arg(Type type, String alternativeName, String description,
507 boolean defaultBoolean, Opt... options)
510 this.description = description;
511 this.defaultBoolValue = defaultBoolean;
512 this.setOptions(options);
513 this.argNames = alternativeName != null
515 { this.getName(), alternativeName }
520 public String argString()
522 return argString(false);
525 public String negateArgString()
527 return argString(true);
530 private String argString(boolean negate)
532 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
533 if (negate && hasOption(Opt.BOOLEAN))
534 sb.append(ArgParser.NEGATESTRING);
535 sb.append(getName());
536 return sb.toString();
539 public String toLongString()
541 StringBuilder sb = new StringBuilder();
542 sb.append(this.getClass().getName()).append('.').append(this.name());
544 if (getNames().length > 0)
546 sb.append(String.join("\", \"", getNames()));
547 if (getNames().length > 0)
550 sb.append("\nType: " + type.name());
551 sb.append("\nOpt: ");
552 // map List<Opt> to List<String> for the String.join
553 List<String> optList = Arrays.asList(argOptions).stream()
554 .map(opt -> opt.name()).collect(Collectors.toList());
555 sb.append(String.join(", ", optList));
557 return sb.toString();
560 public String[] getNames()
565 public String getName()
567 String name = hasOption(Opt.PRESERVECASE) ? this.name()
568 : this.name().toLowerCase(Locale.ROOT);
569 return name.replace('_', '-');
573 public final String toString()
578 public boolean hasOption(Opt o)
580 if (argOptions == null)
582 for (Opt option : argOptions)
590 public boolean hasAllOptions(Opt... opts)
594 if (!this.hasOption(o))
600 protected Opt[] getOptions()
605 protected void setOptions(Opt... options)
607 this.argOptions = options;
610 protected boolean getDefaultBoolValue()
612 return defaultBoolValue;
615 public Type getType()
620 protected String getDescription()
625 public static String booleanArgString(Arg a)
627 StringBuilder sb = new StringBuilder(a.argString());
628 if (a.hasOption(Opt.BOOLEAN))
631 sb.append(a.negateArgString());
633 return sb.toString();
636 public static final String usage()
641 public static final void appendUsageGeneral(StringBuilder sb,
644 for (Type t : EnumSet.allOf(Type.class))
646 if (t.description() != null)
648 StringBuilder argSb = new StringBuilder();
649 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
650 .append(t.name().toLowerCase(Locale.ROOT));
651 appendArgAndDescription(sb, argSb.toString(),
652 "Help for " + t.description(), null, maxArgLength);
653 sb.append(System.lineSeparator());
658 public static final String usage(List<Type> types)
660 StringBuilder sb = new StringBuilder();
662 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
663 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
664 + "] file(s)] [args]");
665 sb.append(System.lineSeparator());
666 sb.append(System.lineSeparator());
668 if (types == null || types.contains(null))
670 // always show --help
671 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
673 sb.append(System.lineSeparator());
675 appendUsageGeneral(sb, DESCRIPTIONINDENT);
679 List<Arg> args = argsSortedForDisplay(types);
682 * just use a set maxArgLength of DESCRIPTIONINDENT
684 int maxArgLength = 0;
687 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
690 String argS = argDisplayString(a);
691 if (argS.length() > maxArgLength)
692 maxArgLength = argS.length();
695 int maxArgLength = DESCRIPTIONINDENT;
697 // always show --help
698 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
699 sb.append(System.lineSeparator());
701 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
703 appendUsageGeneral(sb, maxArgLength);
706 Iterator<Arg> argsI = args.iterator();
707 Type typeSection = null;
708 while (argsI.hasNext())
710 Arg a = argsI.next();
712 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
718 if (a.getType() != typeSection)
720 typeSection = a.getType();
721 String typeDescription = a.getType().description();
722 if (typeDescription != null && typeDescription.length() > 0)
724 // typeDescription = typeDescription.substring(0,
725 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
726 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
727 sb.append(typeDescription);
728 sb.append(System.lineSeparator());
729 sb.append(System.lineSeparator());
733 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
737 sb.append(System.lineSeparator());
741 return sb.toString();
744 private static void appendArgUsage(StringBuilder sb, Arg a,
745 int maxArgLength, int maxWidth)
747 boolean first = appendArgAndDescription(sb, null, null, a,
749 List<String> options = new ArrayList<>();
751 for (Opt o : EnumSet.allOf(Opt.class))
753 if (a.hasOption(o) && o.description() != null)
755 options.add(o.description());
759 final String optDisplaySeparator = "; ";
760 if (options.size() > 0)
763 String spacing = String.format("%-"
764 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
768 sb.append(ARGDESCRIPTIONSEPARATOR);
769 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
774 linelength += spacing.length();
776 if (options.size() > 0)
778 boolean optFirst = true;
779 Iterator<String> optionsI = options.listIterator();
780 while (optionsI.hasNext())
782 String desc = optionsI.next();
788 int descLength = desc.length()
789 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
790 if (linelength + descLength > maxWidth)
792 sb.append(System.lineSeparator());
795 linelength += spacing.length();
797 // sb.append(linelength + "+" + desc.length() + " ");
799 linelength += desc.length();
800 if (optionsI.hasNext())
802 sb.append(optDisplaySeparator);
803 linelength += optDisplaySeparator.length();
808 sb.append(System.lineSeparator());
813 public static String argDisplayString(Arg a)
815 StringBuilder argSb = new StringBuilder();
817 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
818 if (a.hasOption(Opt.STRING))
820 if (a.hasOption(Opt.PREFIXKEV))
822 argSb.append("key=value");
826 argSb.append("=value");
829 return argSb.toString();
832 public static boolean appendArgAndDescription(StringBuilder sb,
833 String aString, String description, Arg a, int maxArgLength)
835 return appendArgAndDescription(sb, aString, description, a,
836 maxArgLength, Platform.consoleWidth());
839 public static boolean appendArgAndDescription(StringBuilder sb,
840 String aString, String description, Arg a, int maxArgLength,
843 if (aString == null && a != null)
845 aString = argDisplayString(a);
847 if (description == null && a != null)
849 description = a.getDescription();
851 sb.append(String.format("%-" + maxArgLength + "s", aString));
852 if (aString.length() > maxArgLength)
854 sb.append(System.lineSeparator());
855 sb.append(String.format("%-" + maxArgLength + "s", ""));
858 int descLength = maxLength - maxArgLength
859 - ARGDESCRIPTIONSEPARATOR.length();
860 // reformat the descriptions lines to the right width
861 Iterator<String> descLines = null;
862 if (description != null)
864 descLines = Arrays.stream(description.split("\\n")).iterator();
866 List<String> splitDescLinesList = new ArrayList<>();
867 while (descLines != null && descLines.hasNext())
869 String line = descLines.next();
870 while (line.length() > descLength)
872 int splitIndex = line.lastIndexOf(" ", descLength);
873 splitDescLinesList.add(line.substring(0, splitIndex));
874 line = line.substring(splitIndex + 1);
876 splitDescLinesList.add(line);
879 Iterator<String> splitDescLines = splitDescLinesList.iterator();
880 boolean first = true;
881 if (splitDescLines != null)
883 while (splitDescLines.hasNext())
887 sb.append(ARGDESCRIPTIONSEPARATOR);
891 sb.append(String.format("%-"
892 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
895 sb.append(splitDescLines.next());
896 sb.append(System.lineSeparator());
903 protected static Iterator<Arg> getAllOfType(Type type)
905 return getAllOfType(type, new Opt[] {});
908 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
910 Opt[] opts = options == null ? new Opt[] {} : options;
911 return EnumSet.allOf(Arg.class).stream().filter(a -> {
912 if (a.getType() != type)
923 private static List<Arg> argsSortedForDisplay(List<Type> types)
925 List<Arg> argsToSort;
926 // if no types provided, do all
927 if (types == null || types.size() == 0 || types.contains(Type.ALL))
930 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
934 argsToSort = new ArrayList<>();
935 for (Type type : types)
939 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
943 Collections.sort(argsToSort, new ArgDisplayComparator());
947 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
949 private static final int DESCRIPTIONINDENT = 20;
953 class ArgDisplayComparator implements Comparator<Arg>
955 private int compareArgOpts(Arg a, Arg b, Opt o)
957 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
958 : (b.hasOption(o) ? 1 : 0);
962 private int compareForDisplay(Arg a, Arg b)
966 // first compare types (in enum order)
967 int i = a.getType().compareTo(b.getType());
970 // do Opt.LAST next (oddly). Reversed args important!
971 i = compareArgOpts(b, a, Opt.LAST);
975 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
977 for (Opt o : optOrder)
979 i = compareArgOpts(a, b, o);
983 // finally order of appearance in enum declarations
984 return a.compareTo(b);
988 public int compare(Arg a, Arg b)
990 return compareForDisplay(a, b);