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 --noheadless or --gui.",
33 Opt.BOOLEAN, 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 are:\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.",
97 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
98 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
99 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
100 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
101 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
102 SORTBYTREE(Type.OPENING,
103 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
104 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
105 ANNOTATIONS(Type.OPENING,
106 "Add an annotations file or URL to the open alignment.",
107 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
108 SHOWANNOTATIONS(Type.OPENING,
109 "Enforces showing (or not showing) alignment annotations.",
110 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
112 "Enforces wrapped (or not wrapped) alignment formatting.",
113 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
114 NOSTRUCTURE(Type.OPENING,
115 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
116 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
118 // Adding a 3D structure
119 STRUCTURE(Type.STRUCTURE,
120 "Load a structure file or URL associated with a sequence in the open alignment.\n"
121 + "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.",
122 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
124 SEQID(Type.STRUCTURE,
125 "Specify the sequence name for the preceding --structure to be associated with.",
126 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
127 PAEMATRIX(Type.STRUCTURE,
128 "Add a PAE json matrix file to the preceding --structure.",
129 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
130 TEMPFAC(Type.STRUCTURE,
131 "Set the type of temperature factor. Possible values are:\n"
132 + "default,\n" + "plddt.",
133 Opt.STRING, Opt.LINKED),
134 STRUCTUREVIEWER(Type.STRUCTURE,
135 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
136 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
138 Opt.STRING, Opt.LINKED, Opt.MULTI),
139 NOTEMPFAC(Type.STRUCTURE,
140 "Do not show the temperature factor annotation for the preceding --structure.",
141 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
143 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
148 "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"
149 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
150 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
151 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
153 "Set the image format for the preceding --image. Valid values are:\n"
154 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
155 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
156 TEXTRENDERER(Type.IMAGE,
157 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
158 + "text,\n" + "lineart.",
159 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
161 "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).",
162 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
164 "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).",
165 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
167 "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).",
168 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
169 STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
170 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
171 Opt.LINKED, Opt.MULTI, Opt.OUTPUTFILE),
172 STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
173 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
174 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
175 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
176 STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
177 "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
178 + "text,\n" + "lineart.",
179 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
180 STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
181 "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).",
182 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
183 STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
184 "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).",
185 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
186 STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
187 "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).",
188 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
191 "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"
192 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
193 + "stockholm (sto, stk),\n" + "pir (pir),\n"
194 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
195 + "pileup (pileup),\n" + "msf (msf),\n"
196 + "clustal (aln),\n" + "phylip (phy),\n"
197 + "jalview (jvp, jar).",
198 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
199 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
201 "Sets the format for the preceding --output file. Valid formats are:\n"
202 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
203 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
204 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
205 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
207 "Process a groovy script in the file for the open alignment.",
208 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
211 "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.",
212 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
213 OVERWRITE(Type.OUTPUT,
214 "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.",
215 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
217 "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.",
218 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
220 // controlling flow of arguments
222 "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.",
223 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
224 SUBSTITUTIONS(Type.FLOW,
225 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
226 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
227 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
228 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
229 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
230 + "{n} - the value of the index counter (starting at 0).\n"
231 + "{++n} - increase and substitute the value of the index counter,\n"
232 + "{} - the value of the current alignment window default index.",
233 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
235 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
236 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
237 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
238 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
239 Opt.ALLOWSUBSTITUTIONS),
240 NPP(Type.FLOW, "n++",
241 "Increase the index counter used in argument value substitutions.",
242 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
244 "Apply the following output arguments to all sets of linked arguments.",
245 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
247 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
248 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
250 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
253 "Secret arg to not quit after --headless mode for tests",
254 Opt.UNARY, Opt.SECRET),
255 ALLSTRUCTURES(Type.FLOW,
256 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
257 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
260 TESTOUTPUT(Type.CONFIG,
261 "Allow specific stdout information. For testing purposes only.",
262 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
263 SETPROP(Type.CONFIG, "Set an individual Java System property.",
264 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
266 "This argument does nothing on its own, but can be used with linkedIds.",
267 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
269 // private options (inserted during arg processing)
270 SETARGFILE(Type.FLOW,
271 "Sets the current value of the argfilename. Inserted before argfilecontents.",
272 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
274 UNSETARGFILE(Type.FLOW,
275 "Unsets the current value of the argfilename. Inserted after argfile contents.",
276 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
278 // these last two have no purpose in the normal Jalview application but are
279 // used by jalview.bin.Launcher to set memory settings. They are not used by
280 // argparser but are here for Usage statement reasons.
281 JVMMEMPC(Type.CONFIG,
282 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
283 + "The equals sign (\"=\") separator must be used with no spaces.",
284 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
285 JVMMEMMAX(Type.CONFIG,
286 "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"
287 + "The equals sign (\"=\") separator must be used with no spaces.",
288 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
292 public static enum Opt
295 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
296 * A default can be given with setOptions(bool, Opt....).
297 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
299 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
300 + ArgParser.NEGATESTRING + "..."),
303 * A STRING Arg will take a value either through --arg=value or --arg value.
305 STRING("expects a value"),
307 * A UNARY Arg is a boolean value, true if present, false if not.
308 * Like BOOLEAN but without the --noarg option.
312 * A MULTI Arg can be specified multiple times.
313 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
315 MULTI("can be specified multiple times"),
317 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
318 * If no linkedId is specified then the current default linkedId will be used.
320 LINKED("is linked to an alignment"),
322 * A NODUPLICATES Arg can only have one value (per linkedId).
323 * The first value will be used and subsequent values ignored with a warning.
325 NODUPLICATEVALUES("cannot have the same value more than once"),
327 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
328 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
329 * See jalview.bin.argparser.BootstrapArgs.
331 BOOTSTRAP("a configuration argument"),
333 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
334 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
335 * but if specified as --arg=filename* then the Java glob expansion method will be used
336 * (see FileUtils.getFilenamesFromGlob()).
337 * Note that this might be different from the shell expansion rules.
339 GLOB("can take multiple filenames with wildcards"),
341 * A NOACTION Arg does not perform a data task,
342 * usually used to control flow in ArgParser.parse(args).
346 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
347 * SubVals and values.
349 ALLOWSUBSTITUTIONS("values can use substitutions"),
351 * A PRIVATE Arg is used internally, and cannot be specified by the user.
355 * A SECRET Arg is used by development processes and although it can be set by the user,
356 * it is not displayed to the user.
360 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
362 ALLOWALL("can be used with " + ArgParser.DOUBLEDASH + "all"),
364 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
365 * the defaultLinkedIdCounter is incremented *first*.
367 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
369 * An INPUT Arg counts as an input for REQUIREINPUT
373 * A REQUIREINPUT Arg can only be applied via --all if there is an input
374 * (i.e. --open or --append)
378 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
379 * --all --output={basename}.ext
381 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
383 * A STORED Arg resets and creates a new set of "opened" linkedIds
387 * A HELP Arg is a --help type arg
389 HELP("provides a help statement"),
391 * A PRIMARY Arg is the main Arg for its type
393 PRIMARY("is a primary argument for its type"),
395 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
399 * A FIRST arg gets moved to appear first in the usage statement (within type)
403 * A LAST arg gets moved to appear last in the usage statement (within type)
407 * After other args are checked, the following args can prefix a KEY=VALUE argument
409 PREFIXKEV("prefixes key=value"),
411 * do not lowercase the name when getting the arg name or arg string
417 private String description;
424 private Opt(String description)
426 this.description = description;
429 public String description()
436 public static enum Type
438 // Type restricts argument to certain usage output
440 CONFIG("arguments used to configure "
441 + ChannelProperties.getProperty("app_name") + " from startup"),
442 OPENING("arguments used to open and format alignments"),
443 STRUCTURE("arguments used to add and format 3D structure data"),
444 PROCESS("arguments used to process an alignment once opened"),
445 OUTPUT("arguments used to save data from a processed alignment"),
446 IMAGE("arguments used to export an image of an alignment"),
447 STRUCTUREIMAGE("arguments used to export an image of an structure"),
448 FLOW("arguments that control processing of the other arguments"), //
449 ALL("all arguments"), // mostly just a place-holder for --help-all
450 NONE, // mostly a place-holder for --help
453 private String description;
460 private Type(String description)
462 this.description = description;
465 public String description()
471 private final String[] argNames;
473 private Opt[] argOptions;
475 private boolean defaultBoolValue;
477 private String description;
481 private Arg(Type type, String description, Opt... options)
483 this(type, null, description, false, options);
486 private Arg(Type type, String description, boolean defaultBoolean,
489 this(type, null, description, defaultBoolean, options);
492 private Arg(Type type, String alternativeName, String description,
495 this(type, alternativeName, description, false, options);
498 private Arg(Type type, String alternativeName, String description,
499 boolean defaultBoolean, Opt... options)
502 this.description = description;
503 this.defaultBoolValue = defaultBoolean;
504 this.setOptions(options);
505 this.argNames = alternativeName != null
507 { this.getName(), alternativeName }
512 public String argString()
514 return argString(false);
517 public String negateArgString()
519 return argString(true);
522 private String argString(boolean negate)
524 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
525 if (negate && hasOption(Opt.BOOLEAN))
526 sb.append(ArgParser.NEGATESTRING);
527 sb.append(getName());
528 return sb.toString();
531 public String toLongString()
533 StringBuilder sb = new StringBuilder();
534 sb.append(this.getClass().getName()).append('.').append(this.name());
536 if (getNames().length > 0)
538 sb.append(String.join("\", \"", getNames()));
539 if (getNames().length > 0)
542 sb.append("\nType: " + type.name());
543 sb.append("\nOpt: ");
544 // map List<Opt> to List<String> for the String.join
545 List<String> optList = Arrays.asList(argOptions).stream()
546 .map(opt -> opt.name()).collect(Collectors.toList());
547 sb.append(String.join(", ", optList));
549 return sb.toString();
552 public String[] getNames()
557 public String getName()
559 String name = hasOption(Opt.PRESERVECASE) ? this.name()
560 : this.name().toLowerCase(Locale.ROOT);
561 return name.replace('_', '-');
565 public final String toString()
570 public boolean hasOption(Opt o)
572 if (argOptions == null)
574 for (Opt option : argOptions)
582 public boolean hasAllOptions(Opt... opts)
586 if (!this.hasOption(o))
592 protected Opt[] getOptions()
597 protected void setOptions(Opt... options)
599 this.argOptions = options;
602 protected boolean getDefaultBoolValue()
604 return defaultBoolValue;
607 public Type getType()
612 protected String getDescription()
617 public static String booleanArgString(Arg a)
619 StringBuilder sb = new StringBuilder(a.argString());
620 if (a.hasOption(Opt.BOOLEAN))
623 sb.append(a.negateArgString());
625 return sb.toString();
628 public static final String usage()
633 public static final void appendUsageGeneral(StringBuilder sb,
636 for (Type t : EnumSet.allOf(Type.class))
638 if (t.description() != null)
640 StringBuilder argSb = new StringBuilder();
641 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
642 .append(t.name().toLowerCase(Locale.ROOT));
643 appendArgAndDescription(sb, argSb.toString(),
644 "Help for " + t.description(), null, maxArgLength);
645 sb.append(System.lineSeparator());
650 public static final String usage(List<Type> types)
652 StringBuilder sb = new StringBuilder();
654 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
655 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
656 + "] file(s)] [args]");
657 sb.append(System.lineSeparator());
658 sb.append(System.lineSeparator());
660 if (types == null || types.contains(null))
662 // always show --help
663 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
665 sb.append(System.lineSeparator());
667 appendUsageGeneral(sb, DESCRIPTIONINDENT);
671 List<Arg> args = argsSortedForDisplay(types);
674 * just use a set maxArgLength of DESCRIPTIONINDENT
676 int maxArgLength = 0;
679 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
682 String argS = argDisplayString(a);
683 if (argS.length() > maxArgLength)
684 maxArgLength = argS.length();
687 int maxArgLength = DESCRIPTIONINDENT;
689 // always show --help
690 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
691 sb.append(System.lineSeparator());
693 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
695 appendUsageGeneral(sb, maxArgLength);
698 Iterator<Arg> argsI = args.iterator();
699 Type typeSection = null;
700 while (argsI.hasNext())
702 Arg a = argsI.next();
704 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
710 if (a.getType() != typeSection)
712 typeSection = a.getType();
713 String typeDescription = a.getType().description();
714 if (typeDescription != null && typeDescription.length() > 0)
716 // typeDescription = typeDescription.substring(0,
717 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
718 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
719 sb.append(typeDescription);
720 sb.append(System.lineSeparator());
721 sb.append(System.lineSeparator());
725 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
729 sb.append(System.lineSeparator());
733 return sb.toString();
736 private static void appendArgUsage(StringBuilder sb, Arg a,
737 int maxArgLength, int maxWidth)
739 boolean first = appendArgAndDescription(sb, null, null, a,
741 List<String> options = new ArrayList<>();
743 for (Opt o : EnumSet.allOf(Opt.class))
745 if (a.hasOption(o) && o.description() != null)
747 options.add(o.description());
751 final String optDisplaySeparator = "; ";
752 if (options.size() > 0)
755 String spacing = String.format("%-"
756 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
760 sb.append(ARGDESCRIPTIONSEPARATOR);
761 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
766 linelength += spacing.length();
768 if (options.size() > 0)
770 boolean optFirst = true;
771 Iterator<String> optionsI = options.listIterator();
772 while (optionsI.hasNext())
774 String desc = optionsI.next();
780 int descLength = desc.length()
781 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
782 if (linelength + descLength > maxWidth)
784 sb.append(System.lineSeparator());
787 linelength += spacing.length();
789 // sb.append(linelength + "+" + desc.length() + " ");
791 linelength += desc.length();
792 if (optionsI.hasNext())
794 sb.append(optDisplaySeparator);
795 linelength += optDisplaySeparator.length();
800 sb.append(System.lineSeparator());
805 public static String argDisplayString(Arg a)
807 StringBuilder argSb = new StringBuilder();
809 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
810 if (a.hasOption(Opt.STRING))
812 if (a.hasOption(Opt.PREFIXKEV))
814 argSb.append("key=value");
818 argSb.append("=value");
821 return argSb.toString();
824 public static boolean appendArgAndDescription(StringBuilder sb,
825 String aString, String description, Arg a, int maxArgLength)
827 return appendArgAndDescription(sb, aString, description, a,
828 maxArgLength, Platform.consoleWidth());
831 public static boolean appendArgAndDescription(StringBuilder sb,
832 String aString, String description, Arg a, int maxArgLength,
835 if (aString == null && a != null)
837 aString = argDisplayString(a);
839 if (description == null && a != null)
841 description = a.getDescription();
843 sb.append(String.format("%-" + maxArgLength + "s", aString));
844 if (aString.length() > maxArgLength)
846 sb.append(System.lineSeparator());
847 sb.append(String.format("%-" + maxArgLength + "s", ""));
850 int descLength = maxLength - maxArgLength
851 - ARGDESCRIPTIONSEPARATOR.length();
852 // reformat the descriptions lines to the right width
853 Iterator<String> descLines = null;
854 if (description != null)
856 descLines = Arrays.stream(description.split("\\n")).iterator();
858 List<String> splitDescLinesList = new ArrayList<>();
859 while (descLines != null && descLines.hasNext())
861 String line = descLines.next();
862 while (line.length() > descLength)
864 int splitIndex = line.lastIndexOf(" ", descLength);
865 splitDescLinesList.add(line.substring(0, splitIndex));
866 line = line.substring(splitIndex + 1);
868 splitDescLinesList.add(line);
871 Iterator<String> splitDescLines = splitDescLinesList.iterator();
872 boolean first = true;
873 if (splitDescLines != null)
875 while (splitDescLines.hasNext())
879 sb.append(ARGDESCRIPTIONSEPARATOR);
883 sb.append(String.format("%-"
884 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
887 sb.append(splitDescLines.next());
888 sb.append(System.lineSeparator());
895 protected static Iterator<Arg> getAllOfType(Type type)
897 return getAllOfType(type, new Opt[] {});
900 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
902 Opt[] opts = options == null ? new Opt[] {} : options;
903 return EnumSet.allOf(Arg.class).stream().filter(a -> {
904 if (a.getType() != type)
915 private static List<Arg> argsSortedForDisplay(List<Type> types)
917 List<Arg> argsToSort;
918 // if no types provided, do all
919 if (types == null || types.size() == 0 || types.contains(Type.ALL))
922 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
926 argsToSort = new ArrayList<>();
927 for (Type type : types)
931 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
935 Collections.sort(argsToSort, new ArgDisplayComparator());
939 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
941 private static final int DESCRIPTIONINDENT = 20;
945 class ArgDisplayComparator implements Comparator<Arg>
947 private int compareArgOpts(Arg a, Arg b, Opt o)
949 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
950 : (b.hasOption(o) ? 1 : 0);
954 private int compareForDisplay(Arg a, Arg b)
958 // first compare types (in enum order)
959 int i = a.getType().compareTo(b.getType());
962 // do Opt.LAST next (oddly). Reversed args important!
963 i = compareArgOpts(b, a, Opt.LAST);
967 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
969 for (Opt o : optOrder)
971 i = compareArgOpts(a, b, o);
975 // finally order of appearance in enum declarations
976 return a.compareTo(b);
980 public int compare(Arg a, Arg b)
982 return compareForDisplay(a, b);