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 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"
98 + "Names of user defined colourschemes will also work,\n"
99 +"and jalview colourscheme specifications like\n"
100 +"--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
101 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
102 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
103 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
104 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
105 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
106 SORTBYTREE(Type.OPENING,
107 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
108 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
109 ANNOTATIONS(Type.OPENING,
110 "Add an annotations file or URL to the open alignment.",
111 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
112 SHOWANNOTATIONS(Type.OPENING,
113 "Enforces showing (or not showing) alignment annotations.",
114 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
116 "Enforces wrapped (or not wrapped) alignment formatting.",
117 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
118 NOSTRUCTURE(Type.OPENING,
119 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
120 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
122 // Adding a 3D structure
123 STRUCTURE(Type.STRUCTURE,
124 "Load a structure file or URL associated with a sequence in the open alignment.\n"
125 + "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.",
126 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
128 SEQID(Type.STRUCTURE,
129 "Specify the sequence name for the preceding --structure to be associated with.",
130 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
131 PAEMATRIX(Type.STRUCTURE,
132 "Add a PAE json matrix file to the preceding --structure.",
133 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
134 TEMPFAC(Type.STRUCTURE,
135 "Set the type of temperature factor. Possible values are:\n"
136 + "default,\n" + "plddt.",
137 Opt.STRING, Opt.LINKED),
138 STRUCTUREVIEWER(Type.STRUCTURE,
139 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
140 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
142 Opt.STRING, Opt.LINKED, Opt.MULTI),
143 NOTEMPFAC(Type.STRUCTURE,
144 "Do not show the temperature factor annotation for the preceding --structure.",
145 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
147 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
152 "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"
153 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
154 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
155 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
157 "Set the image format for the preceding --image. Valid values are:\n"
158 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
159 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
160 TEXTRENDERER(Type.IMAGE,
161 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
162 + "text,\n" + "lineart.",
163 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
165 "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).",
166 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
168 "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).",
169 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
171 "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).",
172 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
173 STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
174 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
175 Opt.LINKED, Opt.MULTI, Opt.OUTPUTFILE),
176 STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
177 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
178 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
179 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
180 STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
181 "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
182 + "text,\n" + "lineart.",
183 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
184 STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
185 "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).",
186 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
187 STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
188 "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).",
189 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
190 STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
191 "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).",
192 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
195 "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"
196 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
197 + "stockholm (sto, stk),\n" + "pir (pir),\n"
198 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
199 + "pileup (pileup),\n" + "msf (msf),\n"
200 + "clustal (aln),\n" + "phylip (phy),\n"
201 + "jalview (jvp, jar).",
202 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
203 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
205 "Sets the format for the preceding --output file. Valid formats are:\n"
206 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
207 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
208 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
209 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
211 "Process a groovy script in the file for the open alignment.",
212 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
215 "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.",
216 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
217 OVERWRITE(Type.OUTPUT,
218 "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.",
219 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
221 "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.",
222 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
224 // controlling flow of arguments
226 "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.",
227 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
228 SUBSTITUTIONS(Type.FLOW,
229 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
230 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
231 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
232 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
233 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
234 + "{n} - the value of the index counter (starting at 0).\n"
235 + "{++n} - increase and substitute the value of the index counter,\n"
236 + "{} - the value of the current alignment window default index.",
237 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
239 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
240 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
241 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
242 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
243 Opt.ALLOWSUBSTITUTIONS),
244 NPP(Type.FLOW, "n++",
245 "Increase the index counter used in argument value substitutions.",
246 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
248 "Apply the following output arguments to all sets of linked arguments.",
249 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
251 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
252 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
254 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
257 "Secret arg to not quit after --headless mode for tests",
258 Opt.UNARY, Opt.SECRET),
259 ALLSTRUCTURES(Type.FLOW,
260 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
261 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
264 TESTOUTPUT(Type.CONFIG,
265 "Allow specific stdout information. For testing purposes only.",
266 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
267 SETPROP(Type.CONFIG, "Set an individual Java System property.",
268 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
270 "This argument does nothing on its own, but can be used with linkedIds.",
271 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
273 // private options (inserted during arg processing)
274 SETARGFILE(Type.FLOW,
275 "Sets the current value of the argfilename. Inserted before argfilecontents.",
276 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
278 UNSETARGFILE(Type.FLOW,
279 "Unsets the current value of the argfilename. Inserted after argfile contents.",
280 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
282 // these last two have no purpose in the normal Jalview application but are
283 // used by jalview.bin.Launcher to set memory settings. They are not used by
284 // argparser but are here for Usage statement reasons.
285 JVMMEMPC(Type.CONFIG,
286 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
287 + "The equals sign (\"=\") separator must be used with no spaces.",
288 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
289 JVMMEMMAX(Type.CONFIG,
290 "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"
291 + "The equals sign (\"=\") separator must be used with no spaces.",
292 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
296 public static enum Opt
299 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
300 * A default can be given with setOptions(bool, Opt....).
301 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
303 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
304 + ArgParser.NEGATESTRING + "..."),
307 * A STRING Arg will take a value either through --arg=value or --arg value.
309 STRING("expects a value"),
311 * A UNARY Arg is a boolean value, true if present, false if not.
312 * Like BOOLEAN but without the --noarg option.
316 * A MULTI Arg can be specified multiple times.
317 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
319 MULTI("can be specified multiple times"),
321 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
322 * If no linkedId is specified then the current default linkedId will be used.
324 LINKED("is linked to an alignment"),
326 * A NODUPLICATES Arg can only have one value (per linkedId).
327 * The first value will be used and subsequent values ignored with a warning.
329 NODUPLICATEVALUES("cannot have the same value more than once"),
331 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
332 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
333 * See jalview.bin.argparser.BootstrapArgs.
335 BOOTSTRAP("a configuration argument"),
337 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
338 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
339 * but if specified as --arg=filename* then the Java glob expansion method will be used
340 * (see FileUtils.getFilenamesFromGlob()).
341 * Note that this might be different from the shell expansion rules.
343 GLOB("can take multiple filenames with wildcards"),
345 * A NOACTION Arg does not perform a data task,
346 * usually used to control flow in ArgParser.parse(args).
350 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
351 * SubVals and values.
353 ALLOWSUBSTITUTIONS("values can use substitutions"),
355 * A PRIVATE Arg is used internally, and cannot be specified by the user.
359 * A SECRET Arg is used by development processes and although it can be set by the user,
360 * it is not displayed to the user.
364 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
366 ALLOWALL("can be used with " + ArgParser.DOUBLEDASH + "all"),
368 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
369 * the defaultLinkedIdCounter is incremented *first*.
371 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
373 * An INPUT Arg counts as an input for REQUIREINPUT
377 * A REQUIREINPUT Arg can only be applied via --all if there is an input
378 * (i.e. --open or --append)
382 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
383 * --all --output={basename}.ext
385 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
387 * A STORED Arg resets and creates a new set of "opened" linkedIds
391 * A HELP Arg is a --help type arg
393 HELP("provides a help statement"),
395 * A PRIMARY Arg is the main Arg for its type
397 PRIMARY("is a primary argument for its type"),
399 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
403 * A FIRST arg gets moved to appear first in the usage statement (within type)
407 * A LAST arg gets moved to appear last in the usage statement (within type)
411 * After other args are checked, the following args can prefix a KEY=VALUE argument
413 PREFIXKEV("prefixes key=value"),
415 * do not lowercase the name when getting the arg name or arg string
421 private String description;
428 private Opt(String description)
430 this.description = description;
433 public String description()
440 public static enum Type
442 // Type restricts argument to certain usage output
444 CONFIG("arguments used to configure "
445 + ChannelProperties.getProperty("app_name") + " from startup"),
446 OPENING("arguments used to open and format alignments"),
447 STRUCTURE("arguments used to add and format 3D structure data"),
448 PROCESS("arguments used to process an alignment once opened"),
449 OUTPUT("arguments used to save data from a processed alignment"),
450 IMAGE("arguments used to export an image of an alignment"),
451 STRUCTUREIMAGE("arguments used to export an image of an structure"),
452 FLOW("arguments that control processing of the other arguments"), //
453 ALL("all arguments"), // mostly just a place-holder for --help-all
454 NONE, // mostly a place-holder for --help
457 private String description;
464 private Type(String description)
466 this.description = description;
469 public String description()
475 private final String[] argNames;
477 private Opt[] argOptions;
479 private boolean defaultBoolValue;
481 private String description;
485 private Arg(Type type, String description, Opt... options)
487 this(type, null, description, false, options);
490 private Arg(Type type, String description, boolean defaultBoolean,
493 this(type, null, description, defaultBoolean, options);
496 private Arg(Type type, String alternativeName, String description,
499 this(type, alternativeName, description, false, options);
502 private Arg(Type type, String alternativeName, String description,
503 boolean defaultBoolean, Opt... options)
506 this.description = description;
507 this.defaultBoolValue = defaultBoolean;
508 this.setOptions(options);
509 this.argNames = alternativeName != null
511 { this.getName(), alternativeName }
516 public String argString()
518 return argString(false);
521 public String negateArgString()
523 return argString(true);
526 private String argString(boolean negate)
528 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
529 if (negate && hasOption(Opt.BOOLEAN))
530 sb.append(ArgParser.NEGATESTRING);
531 sb.append(getName());
532 return sb.toString();
535 public String toLongString()
537 StringBuilder sb = new StringBuilder();
538 sb.append(this.getClass().getName()).append('.').append(this.name());
540 if (getNames().length > 0)
542 sb.append(String.join("\", \"", getNames()));
543 if (getNames().length > 0)
546 sb.append("\nType: " + type.name());
547 sb.append("\nOpt: ");
548 // map List<Opt> to List<String> for the String.join
549 List<String> optList = Arrays.asList(argOptions).stream()
550 .map(opt -> opt.name()).collect(Collectors.toList());
551 sb.append(String.join(", ", optList));
553 return sb.toString();
556 public String[] getNames()
561 public String getName()
563 String name = hasOption(Opt.PRESERVECASE) ? this.name()
564 : this.name().toLowerCase(Locale.ROOT);
565 return name.replace('_', '-');
569 public final String toString()
574 public boolean hasOption(Opt o)
576 if (argOptions == null)
578 for (Opt option : argOptions)
586 public boolean hasAllOptions(Opt... opts)
590 if (!this.hasOption(o))
596 protected Opt[] getOptions()
601 protected void setOptions(Opt... options)
603 this.argOptions = options;
606 protected boolean getDefaultBoolValue()
608 return defaultBoolValue;
611 public Type getType()
616 protected String getDescription()
621 public static String booleanArgString(Arg a)
623 StringBuilder sb = new StringBuilder(a.argString());
624 if (a.hasOption(Opt.BOOLEAN))
627 sb.append(a.negateArgString());
629 return sb.toString();
632 public static final String usage()
637 public static final void appendUsageGeneral(StringBuilder sb,
640 for (Type t : EnumSet.allOf(Type.class))
642 if (t.description() != null)
644 StringBuilder argSb = new StringBuilder();
645 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
646 .append(t.name().toLowerCase(Locale.ROOT));
647 appendArgAndDescription(sb, argSb.toString(),
648 "Help for " + t.description(), null, maxArgLength);
649 sb.append(System.lineSeparator());
654 public static final String usage(List<Type> types)
656 StringBuilder sb = new StringBuilder();
658 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
659 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
660 + "] file(s)] [args]");
661 sb.append(System.lineSeparator());
662 sb.append(System.lineSeparator());
664 if (types == null || types.contains(null))
666 // always show --help
667 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
669 sb.append(System.lineSeparator());
671 appendUsageGeneral(sb, DESCRIPTIONINDENT);
675 List<Arg> args = argsSortedForDisplay(types);
678 * just use a set maxArgLength of DESCRIPTIONINDENT
680 int maxArgLength = 0;
683 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
686 String argS = argDisplayString(a);
687 if (argS.length() > maxArgLength)
688 maxArgLength = argS.length();
691 int maxArgLength = DESCRIPTIONINDENT;
693 // always show --help
694 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
695 sb.append(System.lineSeparator());
697 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
699 appendUsageGeneral(sb, maxArgLength);
702 Iterator<Arg> argsI = args.iterator();
703 Type typeSection = null;
704 while (argsI.hasNext())
706 Arg a = argsI.next();
708 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
714 if (a.getType() != typeSection)
716 typeSection = a.getType();
717 String typeDescription = a.getType().description();
718 if (typeDescription != null && typeDescription.length() > 0)
720 // typeDescription = typeDescription.substring(0,
721 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
722 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
723 sb.append(typeDescription);
724 sb.append(System.lineSeparator());
725 sb.append(System.lineSeparator());
729 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
733 sb.append(System.lineSeparator());
737 return sb.toString();
740 private static void appendArgUsage(StringBuilder sb, Arg a,
741 int maxArgLength, int maxWidth)
743 boolean first = appendArgAndDescription(sb, null, null, a,
745 List<String> options = new ArrayList<>();
747 for (Opt o : EnumSet.allOf(Opt.class))
749 if (a.hasOption(o) && o.description() != null)
751 options.add(o.description());
755 final String optDisplaySeparator = "; ";
756 if (options.size() > 0)
759 String spacing = String.format("%-"
760 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
764 sb.append(ARGDESCRIPTIONSEPARATOR);
765 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
770 linelength += spacing.length();
772 if (options.size() > 0)
774 boolean optFirst = true;
775 Iterator<String> optionsI = options.listIterator();
776 while (optionsI.hasNext())
778 String desc = optionsI.next();
784 int descLength = desc.length()
785 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
786 if (linelength + descLength > maxWidth)
788 sb.append(System.lineSeparator());
791 linelength += spacing.length();
793 // sb.append(linelength + "+" + desc.length() + " ");
795 linelength += desc.length();
796 if (optionsI.hasNext())
798 sb.append(optDisplaySeparator);
799 linelength += optDisplaySeparator.length();
804 sb.append(System.lineSeparator());
809 public static String argDisplayString(Arg a)
811 StringBuilder argSb = new StringBuilder();
813 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
814 if (a.hasOption(Opt.STRING))
816 if (a.hasOption(Opt.PREFIXKEV))
818 argSb.append("key=value");
822 argSb.append("=value");
825 return argSb.toString();
828 public static boolean appendArgAndDescription(StringBuilder sb,
829 String aString, String description, Arg a, int maxArgLength)
831 return appendArgAndDescription(sb, aString, description, a,
832 maxArgLength, Platform.consoleWidth());
835 public static boolean appendArgAndDescription(StringBuilder sb,
836 String aString, String description, Arg a, int maxArgLength,
839 if (aString == null && a != null)
841 aString = argDisplayString(a);
843 if (description == null && a != null)
845 description = a.getDescription();
847 sb.append(String.format("%-" + maxArgLength + "s", aString));
848 if (aString.length() > maxArgLength)
850 sb.append(System.lineSeparator());
851 sb.append(String.format("%-" + maxArgLength + "s", ""));
854 int descLength = maxLength - maxArgLength
855 - ARGDESCRIPTIONSEPARATOR.length();
856 // reformat the descriptions lines to the right width
857 Iterator<String> descLines = null;
858 if (description != null)
860 descLines = Arrays.stream(description.split("\\n")).iterator();
862 List<String> splitDescLinesList = new ArrayList<>();
863 while (descLines != null && descLines.hasNext())
865 String line = descLines.next();
866 while (line.length() > descLength)
868 int splitIndex = line.lastIndexOf(" ", descLength);
869 splitDescLinesList.add(line.substring(0, splitIndex));
870 line = line.substring(splitIndex + 1);
872 splitDescLinesList.add(line);
875 Iterator<String> splitDescLines = splitDescLinesList.iterator();
876 boolean first = true;
877 if (splitDescLines != null)
879 while (splitDescLines.hasNext())
883 sb.append(ARGDESCRIPTIONSEPARATOR);
887 sb.append(String.format("%-"
888 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
891 sb.append(splitDescLines.next());
892 sb.append(System.lineSeparator());
899 protected static Iterator<Arg> getAllOfType(Type type)
901 return getAllOfType(type, new Opt[] {});
904 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
906 Opt[] opts = options == null ? new Opt[] {} : options;
907 return EnumSet.allOf(Arg.class).stream().filter(a -> {
908 if (a.getType() != type)
919 private static List<Arg> argsSortedForDisplay(List<Type> types)
921 List<Arg> argsToSort;
922 // if no types provided, do all
923 if (types == null || types.size() == 0 || types.contains(Type.ALL))
926 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
930 argsToSort = new ArrayList<>();
931 for (Type type : types)
935 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
939 Collections.sort(argsToSort, new ArgDisplayComparator());
943 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
945 private static final int DESCRIPTIONINDENT = 20;
949 class ArgDisplayComparator implements Comparator<Arg>
951 private int compareArgOpts(Arg a, Arg b, Opt o)
953 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
954 : (b.hasOption(o) ? 1 : 0);
958 private int compareForDisplay(Arg a, Arg b)
962 // first compare types (in enum order)
963 int i = a.getType().compareTo(b.getType());
966 // do Opt.LAST next (oddly). Reversed args important!
967 i = compareArgOpts(b, a, Opt.LAST);
971 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
973 for (Opt o : optOrder)
975 i = compareArgOpts(a, b, o);
979 // finally order of appearance in enum declarations
980 return a.compareTo(b);
984 public int compare(Arg a, Arg b)
986 return compareForDisplay(a, b);