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 USAGESTATS(Type.CONFIG,
48 "Send (or don't send) initial launch usage stats.", true,
49 Opt.BOOLEAN, Opt.BOOTSTRAP),
50 WEBSERVICEDISCOVERY(Type.CONFIG,
51 "Attempt (or don't attempt) to connect to JABAWS web services.",
52 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
54 "Use a file as the preferences file instead of the usual ~/"
55 + ChannelProperties.getProperty("preferences.filename")
57 Opt.STRING, Opt.BOOTSTRAP),
58 DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
60 TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
61 Opt.BOOTSTRAP, Opt.SECRET),
62 QUIET(Type.CONFIG, "q",
63 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
64 Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
65 INITSUBSTITUTIONS(Type.CONFIG,
66 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
67 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
69 // Opening an alignment
71 "Opens one or more alignment files or URLs in new alignment windows.",
72 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
73 Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.STORED,
76 "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
77 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
78 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
80 "Specifies the title for the open alignment window as string.",
81 Opt.STRING, Opt.LINKED),
82 COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
83 "Applies the colour scheme to the open alignment window. Valid values are:\n"
84 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
85 + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
86 + "gecos-blossom,\n" + "gecos-sunset,\n"
87 + "gecos-ocean,\n" + "hydrophobic,\n"
88 + "helix-propensity,\n" + "strand-propensity,\n"
89 + "turn-propensity,\n" + "buried-index,\n"
90 + "nucleotide,\n" + "nucleotide-ambiguity,\n"
91 + "purine-pyrimidine,\n" + "rna-helices,\n"
92 + "t-coffee-scores,\n" + "sequence-id.",
93 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
94 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
95 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
96 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
97 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
98 SORTBYTREE(Type.OPENING,
99 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
100 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
101 ANNOTATIONS(Type.OPENING,
102 "Add an annotations file or URL to the open alignment.",
103 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
104 SHOWANNOTATIONS(Type.OPENING,
105 "Enforces showing (or not showing) alignment annotations.",
106 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
108 "Enforces wrapped (or not wrapped) alignment formatting.",
109 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
110 NOSTRUCTURE(Type.OPENING,
111 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
112 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
114 // Adding a 3D structure
115 STRUCTURE(Type.STRUCTURE,
116 "Load a structure file or URL associated with a sequence in the open alignment.\n"
117 + "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.",
118 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
120 SEQID(Type.STRUCTURE,
121 "Specify the sequence name for the preceding --structure to be associated with.",
122 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
123 PAEMATRIX(Type.STRUCTURE,
124 "Add a PAE json matrix file to the preceding --structure.",
125 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
126 TEMPFAC(Type.STRUCTURE,
127 "Set the type of temperature factor. Possible values are:\n"
128 + "default,\n" + "plddt.",
129 Opt.STRING, Opt.LINKED),
130 STRUCTUREVIEWER(Type.STRUCTURE,
131 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
132 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
134 Opt.STRING, Opt.LINKED, Opt.MULTI),
135 NOTEMPFAC(Type.STRUCTURE,
136 "Do not show the temperature factor annotation for the preceding --structure.",
137 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
139 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
144 "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"
145 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
146 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
147 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
149 "Set the image format for the preceding --image. Valid values are:\n"
150 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
151 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
152 TEXTRENDERER(Type.IMAGE,
153 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
154 + "text,\n" + "lineart.",
155 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
157 "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).",
158 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
160 "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).",
161 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
163 "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).",
164 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
165 STRUCTUREIMAGE(Type.STRUCTUREIMAGE,
166 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
167 Opt.LINKED, Opt.MULTI, Opt.OUTPUTFILE),
168 STRUCTUREIMAGETYPE(Type.STRUCTUREIMAGE,
169 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
170 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
171 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
172 STRUCTUREIMAGETEXTRENDERER(Type.STRUCTUREIMAGE,
173 "Sets whether text in a vector structure image format (SVG, EPS) should be rendered as text or vector line-art. Possible values are:\n"
174 + "text,\n" + "lineart.",
175 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
176 STRUCTUREIMAGESCALE(Type.STRUCTUREIMAGE,
177 "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).",
178 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
179 STRUCTUREIMAGEWIDTH(Type.STRUCTUREIMAGE,
180 "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).",
181 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
182 STRUCTUREIMAGEHEIGHT(Type.STRUCTUREIMAGE,
183 "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).",
184 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
187 "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"
188 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
189 + "stockholm (sto, stk),\n" + "pir (pir),\n"
190 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
191 + "pileup (pileup),\n" + "msf (msf),\n"
192 + "clustal (aln),\n" + "phylip (phy),\n"
193 + "jalview (jvp, jar).",
194 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
195 Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
197 "Sets the format for the preceding --output file. Valid formats are:\n"
198 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
199 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
200 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
201 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
203 "Process a groovy script in the file for the open alignment.",
204 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
207 "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.",
208 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
209 OVERWRITE(Type.OUTPUT,
210 "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.",
211 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
213 "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.",
214 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
216 // controlling flow of arguments
218 "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.",
219 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
220 SUBSTITUTIONS(Type.FLOW,
221 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
222 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
223 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
224 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
225 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
226 + "{n} - the value of the index counter (starting at 0).\n"
227 + "{++n} - increase and substitute the value of the index counter,\n"
228 + "{} - the value of the current alignment window default index.",
229 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
231 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
232 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
233 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
234 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
235 Opt.ALLOWSUBSTITUTIONS),
236 NPP(Type.FLOW, "n++",
237 "Increase the index counter used in argument value substitutions.",
238 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
240 "Apply the following output arguments to all sets of linked arguments.",
241 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
243 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
244 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
246 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
249 "Secret arg to not quit after --headless mode for tests",
250 Opt.UNARY, Opt.SECRET),
251 ALLSTRUCTURES(Type.FLOW,
252 "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
253 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
256 TESTOUTPUT(Type.CONFIG,
257 "Allow specific stdout information. For testing purposes only.",
258 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
259 SETPROP(Type.CONFIG, "Set an individual Java System property.",
260 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
262 "This argument does nothing on its own, but can be used with linkedIds.",
263 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
265 // private options (inserted during arg processing)
266 SETARGFILE(Type.FLOW,
267 "Sets the current value of the argfilename. Inserted before argfilecontents.",
268 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
270 UNSETARGFILE(Type.FLOW,
271 "Unsets the current value of the argfilename. Inserted after argfile contents.",
272 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
274 // these last two have no purpose in the normal Jalview application but are
275 // used by jalview.bin.Launcher to set memory settings. They are not used by
276 // argparser but are here for Usage statement reasons.
277 JVMMEMPC(Type.CONFIG,
278 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
279 + "The equals sign (\"=\") separator must be used with no spaces.",
280 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
281 JVMMEMMAX(Type.CONFIG,
282 "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"
283 + "The equals sign (\"=\") separator must be used with no spaces.",
284 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
288 public static enum Opt
291 * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
292 * A default can be given with setOptions(bool, Opt....).
293 * Use ArgParser.isSet(Arg) to see if this arg was not specified.
295 BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
296 + ArgParser.NEGATESTRING + "..."),
299 * A STRING Arg will take a value either through --arg=value or --arg value.
301 STRING("expects a value"),
303 * A UNARY Arg is a boolean value, true if present, false if not.
304 * Like BOOLEAN but without the --noarg option.
308 * A MULTI Arg can be specified multiple times.
309 * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
311 MULTI("can be specified multiple times"),
313 * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
314 * If no linkedId is specified then the current default linkedId will be used.
316 LINKED("is linked to an alignment"),
318 * A NODUPLICATES Arg can only have one value (per linkedId).
319 * The first value will be used and subsequent values ignored with a warning.
321 NODUPLICATEVALUES("cannot have the same value more than once"),
323 * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
324 * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
325 * See jalview.bin.argparser.BootstrapArgs.
327 BOOTSTRAP("a configuration argument"),
329 * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
330 * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
331 * but if specified as --arg=filename* then the Java glob expansion method will be used
332 * (see FileUtils.getFilenamesFromGlob()).
333 * Note that this might be different from the shell expansion rules.
335 GLOB("can take multiple filenames with wildcards"),
337 * A NOACTION Arg does not perform a data task,
338 * usually used to control flow in ArgParser.parse(args).
342 * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
343 * SubVals and values.
345 ALLOWSUBSTITUTIONS("values can use substitutions"),
347 * A PRIVATE Arg is used internally, and cannot be specified by the user.
351 * A SECRET Arg is used by development processes and although it can be set by the user,
352 * it is not displayed to the user.
356 * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
358 ALLOWALL("can be used with " + ArgParser.DOUBLEDASH + "all"),
360 * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
361 * the defaultLinkedIdCounter is incremented *first*.
363 INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
365 * An INPUT Arg counts as an input for REQUIREINPUT
369 * A REQUIREINPUT Arg can only be applied via --all if there is an input
370 * (i.e. --open or --append)
374 * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
375 * --all --output={basename}.ext
377 OUTPUTFILE("output file --headless will be assumed unless --gui used"),
379 * A STORED Arg resets and creates a new set of "opened" linkedIds
383 * A HELP Arg is a --help type arg
385 HELP("provides a help statement"),
387 * A PRIMARY Arg is the main Arg for its type
389 PRIMARY("is a primary argument for its type"),
391 * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
395 * A FIRST arg gets moved to appear first in the usage statement (within type)
399 * A LAST arg gets moved to appear last in the usage statement (within type)
405 private String description;
412 private Opt(String description)
414 this.description = description;
417 public String description()
424 public static enum Type
426 // Type restricts argument to certain usage output
428 CONFIG("arguments used to configure "
429 + ChannelProperties.getProperty("app_name") + " from startup"),
430 OPENING("arguments used to open and format alignments"),
431 STRUCTURE("arguments used to add and format 3D structure data"),
432 PROCESS("arguments used to process an alignment once opened"),
433 OUTPUT("arguments used to save data from a processed alignment"),
434 IMAGE("arguments used to export an image of an alignment"),
435 STRUCTUREIMAGE("arguments used to export an image of an structure"),
436 FLOW("arguments that control processing of the other arguments"), //
437 ALL("all arguments"), // mostly just a place-holder for --help-all
438 NONE, // mostly a place-holder for --help
441 private String description;
448 private Type(String description)
450 this.description = description;
453 public String description()
459 private final String[] argNames;
461 private Opt[] argOptions;
463 private boolean defaultBoolValue;
465 private String description;
469 private Arg(Type type, String description, Opt... options)
471 this(type, null, description, false, options);
474 private Arg(Type type, String description, boolean defaultBoolean,
477 this(type, null, description, defaultBoolean, options);
480 private Arg(Type type, String alternativeName, String description,
483 this(type, alternativeName, description, false, options);
486 private Arg(Type type, String alternativeName, String description,
487 boolean defaultBoolean, Opt... options)
489 this.argNames = alternativeName != null
491 { this.getName(), alternativeName }
495 this.description = description;
496 this.defaultBoolValue = defaultBoolean;
497 this.setOptions(options);
500 public String argString()
502 return argString(false);
505 public String negateArgString()
507 return argString(true);
510 private String argString(boolean negate)
512 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
513 if (negate && hasOption(Opt.BOOLEAN))
514 sb.append(ArgParser.NEGATESTRING);
515 sb.append(getName());
516 return sb.toString();
519 public String toLongString()
521 StringBuilder sb = new StringBuilder();
522 sb.append(this.getClass().getName()).append('.').append(this.name());
524 if (getNames().length > 0)
526 sb.append(String.join("\", \"", getNames()));
527 if (getNames().length > 0)
530 sb.append("\nType: " + type.name());
531 sb.append("\nOpt: ");
532 // map List<Opt> to List<String> for the String.join
533 List<String> optList = Arrays.asList(argOptions).stream()
534 .map(opt -> opt.name()).collect(Collectors.toList());
535 sb.append(String.join(", ", optList));
537 return sb.toString();
540 public String[] getNames()
545 public String getName()
547 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
551 public final String toString()
556 public boolean hasOption(Opt o)
558 if (argOptions == null)
560 for (Opt option : argOptions)
568 public boolean hasAllOptions(Opt... opts)
572 if (!this.hasOption(o))
578 protected Opt[] getOptions()
583 protected void setOptions(Opt... options)
585 this.argOptions = options;
588 protected boolean getDefaultBoolValue()
590 return defaultBoolValue;
593 public Type getType()
598 protected String getDescription()
603 public static String booleanArgString(Arg a)
605 StringBuilder sb = new StringBuilder(a.argString());
606 if (a.hasOption(Opt.BOOLEAN))
609 sb.append(a.negateArgString());
611 return sb.toString();
614 public static final String usage()
619 public static final void appendUsageGeneral(StringBuilder sb,
622 for (Type t : EnumSet.allOf(Type.class))
624 if (t.description() != null)
626 StringBuilder argSb = new StringBuilder();
627 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
628 .append(t.name().toLowerCase(Locale.ROOT));
629 appendArgAndDescription(sb, argSb.toString(),
630 "Help for " + t.description(), null, maxArgLength);
631 sb.append(System.lineSeparator());
636 public static final String usage(List<Type> types)
638 StringBuilder sb = new StringBuilder();
640 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
641 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
642 + "] file(s)] [args]");
643 sb.append(System.lineSeparator());
644 sb.append(System.lineSeparator());
646 if (types == null || types.contains(null))
648 // always show --help
649 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
651 sb.append(System.lineSeparator());
653 appendUsageGeneral(sb, DESCRIPTIONINDENT);
657 List<Arg> args = argsSortedForDisplay(types);
660 * just use a set maxArgLength of DESCRIPTIONINDENT
662 int maxArgLength = 0;
665 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
668 String argS = argDisplayString(a);
669 if (argS.length() > maxArgLength)
670 maxArgLength = argS.length();
673 int maxArgLength = DESCRIPTIONINDENT;
675 // always show --help
676 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
677 sb.append(System.lineSeparator());
679 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
681 appendUsageGeneral(sb, maxArgLength);
684 Iterator<Arg> argsI = args.iterator();
685 Type typeSection = null;
686 while (argsI.hasNext())
688 Arg a = argsI.next();
690 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
696 if (a.getType() != typeSection)
698 typeSection = a.getType();
699 String typeDescription = a.getType().description();
700 if (typeDescription != null && typeDescription.length() > 0)
702 // typeDescription = typeDescription.substring(0,
703 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
704 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
705 sb.append(typeDescription);
706 sb.append(System.lineSeparator());
707 sb.append(System.lineSeparator());
711 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
715 sb.append(System.lineSeparator());
719 return sb.toString();
722 private static void appendArgUsage(StringBuilder sb, Arg a,
723 int maxArgLength, int maxWidth)
725 boolean first = appendArgAndDescription(sb, null, null, a,
727 List<String> options = new ArrayList<>();
729 for (Opt o : EnumSet.allOf(Opt.class))
731 if (a.hasOption(o) && o.description() != null)
733 options.add(o.description());
737 final String optDisplaySeparator = "; ";
738 if (options.size() > 0)
741 String spacing = String.format("%-"
742 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
746 sb.append(ARGDESCRIPTIONSEPARATOR);
747 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
752 linelength += spacing.length();
754 if (options.size() > 0)
756 boolean optFirst = true;
757 Iterator<String> optionsI = options.listIterator();
758 while (optionsI.hasNext())
760 String desc = optionsI.next();
766 int descLength = desc.length()
767 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
768 if (linelength + descLength > maxWidth)
770 sb.append(System.lineSeparator());
773 linelength += spacing.length();
775 // sb.append(linelength + "+" + desc.length() + " ");
777 linelength += desc.length();
778 if (optionsI.hasNext())
780 sb.append(optDisplaySeparator);
781 linelength += optDisplaySeparator.length();
786 sb.append(System.lineSeparator());
791 public static String argDisplayString(Arg a)
793 StringBuilder argSb = new StringBuilder();
795 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
796 if (a.hasOption(Opt.STRING))
797 argSb.append("=value");
798 return argSb.toString();
801 public static boolean appendArgAndDescription(StringBuilder sb,
802 String aString, String description, Arg a, int maxArgLength)
804 return appendArgAndDescription(sb, aString, description, a,
805 maxArgLength, Platform.consoleWidth());
808 public static boolean appendArgAndDescription(StringBuilder sb,
809 String aString, String description, Arg a, int maxArgLength,
812 if (aString == null && a != null)
814 aString = argDisplayString(a);
816 if (description == null && a != null)
818 description = a.getDescription();
820 sb.append(String.format("%-" + maxArgLength + "s", aString));
821 if (aString.length() > maxArgLength)
823 sb.append(System.lineSeparator());
824 sb.append(String.format("%-" + maxArgLength + "s", ""));
827 int descLength = maxLength - maxArgLength
828 - ARGDESCRIPTIONSEPARATOR.length();
829 // reformat the descriptions lines to the right width
830 Iterator<String> descLines = null;
831 if (description != null)
833 descLines = Arrays.stream(description.split("\\n")).iterator();
835 List<String> splitDescLinesList = new ArrayList<>();
836 while (descLines != null && descLines.hasNext())
838 String line = descLines.next();
839 while (line.length() > descLength)
841 int splitIndex = line.lastIndexOf(" ", descLength);
842 splitDescLinesList.add(line.substring(0, splitIndex));
843 line = line.substring(splitIndex + 1);
845 splitDescLinesList.add(line);
848 Iterator<String> splitDescLines = splitDescLinesList.iterator();
849 boolean first = true;
850 if (splitDescLines != null)
852 while (splitDescLines.hasNext())
856 sb.append(ARGDESCRIPTIONSEPARATOR);
860 sb.append(String.format("%-"
861 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
864 sb.append(splitDescLines.next());
865 sb.append(System.lineSeparator());
872 protected static Iterator<Arg> getAllOfType(Type type)
874 return getAllOfType(type, new Opt[] {});
877 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
879 Opt[] opts = options == null ? new Opt[] {} : options;
880 return EnumSet.allOf(Arg.class).stream().filter(a -> {
881 if (a.getType() != type)
892 private static List<Arg> argsSortedForDisplay(List<Type> types)
894 List<Arg> argsToSort;
895 // if no types provided, do all
896 if (types == null || types.size() == 0 || types.contains(Type.ALL))
899 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
903 argsToSort = new ArrayList<>();
904 for (Type type : types)
908 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
912 Collections.sort(argsToSort, new ArgDisplayComparator());
916 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
918 private static final int DESCRIPTIONINDENT = 20;
922 class ArgDisplayComparator implements Comparator<Arg>
924 private int compareArgOpts(Arg a, Arg b, Opt o)
926 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
927 : (b.hasOption(o) ? 1 : 0);
931 private int compareForDisplay(Arg a, Arg b)
935 // first compare types (in enum order)
936 int i = a.getType().compareTo(b.getType());
939 // do Opt.LAST next (oddly). Reversed args important!
940 i = compareArgOpts(b, a, Opt.LAST);
944 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
946 for (Opt o : optOrder)
948 i = compareArgOpts(a, b, o);
952 // finally order of appearance in enum declarations
953 return a.compareTo(b);
957 public int compare(Arg a, Arg b)
959 return compareForDisplay(a, b);