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 Opt.UNARY, Opt.BOOTSTRAP),
33 JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
34 Opt.STRING, Opt.BOOTSTRAP),
35 NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
36 Opt.BOOLEAN, Opt.BOOTSTRAP),
38 "Show (or don't show) the About Jalview splash screen.", true,
39 Opt.BOOLEAN, Opt.BOOTSTRAP),
40 QUESTIONNAIRE(Type.CONFIG,
41 "Show (or don't show) the questionnaire if one is available.",
42 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
43 USAGESTATS(Type.CONFIG,
44 "Send (or don't send) initial launch usage stats.", true,
45 Opt.BOOLEAN, Opt.BOOTSTRAP),
46 WEBSERVICEDISCOVERY(Type.CONFIG,
47 "Attempt (or don't attempt) to connect to JABAWS web services.",
48 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
50 "Use a file as the preferences file instead of the usual ~/"
51 + ChannelProperties.getProperty("preferences.filename")
53 Opt.STRING, Opt.BOOTSTRAP),
54 DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
56 TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
57 Opt.BOOTSTRAP, Opt.SECRET),
58 QUIET(Type.CONFIG, "q",
59 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
60 Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
61 INITSUBSTITUTIONS(Type.CONFIG,
62 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
63 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
65 // Opening an alignment
67 "Opens one or more alignment files or URLs in new alignment windows.",
68 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
69 Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.STORED,
72 "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
73 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
74 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
76 "Specifies the title for the open alignment window as string.",
77 Opt.STRING, Opt.LINKED),
78 COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
79 "Applies the colour scheme to the open alignment window. Valid values are:\n"
80 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
81 + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
82 + "gecos-blossom,\n" + "gecos-sunset,\n"
83 + "gecos-ocean,\n" + "hydrophobic,\n"
84 + "helix-propensity,\n" + "strand-propensity,\n"
85 + "turn-propensity,\n" + "buried-index,\n"
86 + "nucleotide,\n" + "nucleotide-ambiguity,\n"
87 + "purine-pyrimidine,\n" + "rna-helices,\n"
88 + "t-coffee-scores,\n" + "sequence-id.",
89 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
90 FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
91 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
92 TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
93 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
94 SORTBYTREE(Type.OPENING,
95 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
96 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
97 ANNOTATIONS(Type.OPENING,
98 "Add an annotations file or URL to the open alignment.",
99 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
100 SHOWANNOTATIONS(Type.OPENING,
101 "Enforces showing (or not showing) alignment annotations.",
102 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
104 "Enforces wrapped (or not wrapped) alignment formatting.",
105 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
106 NOSTRUCTURE(Type.OPENING,
107 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
108 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
110 // Adding a 3D structure
111 STRUCTURE(Type.STRUCTURE,
112 "Load a structure file or URL associated with a sequence in the open alignment.\n"
113 + "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.",
114 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
116 SEQID(Type.STRUCTURE,
117 "Specify the sequence name for the preceding --structure to be associated with.",
118 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
119 PAEMATRIX(Type.STRUCTURE,
120 "Add a PAE json matrix file to the preceding --structure.",
121 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
122 TEMPFAC(Type.STRUCTURE,
123 "Set the type of temperature factor. Possible values are:\n"
124 + "default,\n" + "plddt.",
125 Opt.STRING, Opt.LINKED),
126 STRUCTUREVIEWER(Type.STRUCTURE,
127 "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values of name are:\n"
128 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
130 Opt.STRING, Opt.LINKED, Opt.MULTI),
131 NOTEMPFAC(Type.STRUCTURE,
132 "Do not show the temperature factor annotation for the preceding --structure.",
133 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
136 SHOWSSANNOTATIONS(Type.STRUCTURE, null, Opt.BOOLEAN, Opt.LINKED,
141 "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"
142 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
143 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
144 Opt.REQUIREINPUT, Opt.OUTPUT, Opt.PRIMARY),
146 "Set the image format for the preceding --image. Valid values are:\n"
147 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
148 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
149 TEXTRENDERER(Type.IMAGE,
150 "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
151 + "text,\n" + "lineart.",
152 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
154 "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).",
155 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
157 "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).",
158 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
160 "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).",
161 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
162 STRUCTUREIMAGE(Type.IMAGE,
163 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
164 Opt.LINKED, Opt.MULTI),
166 STRUCTUREIMAGETYPE(Type.IMAGE,
167 "Set the structure image format for the preceding --structureimage. Valid values are:\n"
168 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
169 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
170 STRUCTUREIMAGETEXTRENDERER(Type.IMAGE,
171 "Sets whether text in a vector structure image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
172 + "text,\n" + "lineart.",
173 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
174 STRUCTUREIMAGESCALE(Type.IMAGE,
175 "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).",
176 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
177 STRUCTUREIMAGEWIDTH(Type.IMAGE,
178 "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).",
179 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
180 STRUCTUREIMAGEHEIGHT(Type.IMAGE,
181 "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).",
182 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
185 "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"
186 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
187 + "stockholm (sto, stk),\n" + "pir (pir),\n"
188 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
189 + "pileup (pileup),\n" + "msf (msf),\n"
190 + "clustal (aln),\n" + "phylip (phy),\n"
191 + "jalview (jvp, jar).",
192 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
193 Opt.REQUIREINPUT, Opt.OUTPUT, Opt.PRIMARY),
195 "Sets the format for the preceding --output file. Valid formats are:\n"
196 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
197 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
198 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
199 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
201 "Process a groovy script in the file for the open alignment.",
202 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
205 "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.",
206 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
207 OVERWRITE(Type.OUTPUT,
208 "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.",
209 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
211 "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.",
212 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
214 // controlling flow of arguments
216 "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.",
217 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
218 SUBSTITUTIONS(Type.FLOW,
219 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
220 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
221 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
222 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
223 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
224 + "{n} - the value of the index counter (starting at 0).\n"
225 + "{++n} - increase and substitute the value of the index counter,\n"
226 + "{} - the value of the current alignment window default index.",
227 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
229 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
230 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
231 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
232 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
233 Opt.ALLOWSUBSTITUTIONS),
234 NPP(Type.FLOW, "n++",
235 "Increase the index counter used in argument value substitutions.",
236 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
238 "Apply the following output arguments to all sets of linked arguments.",
239 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
241 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
242 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
244 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
248 TESTOUTPUT(Type.CONFIG,
249 "Allow specific stdout information. For testing purposes only.",
250 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
251 SETPROP(Type.CONFIG, "Set an individual Java System property.",
252 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
254 "This argument does nothing on its own, but can be used with linkedIds.",
255 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
257 // private options (inserted during arg processing)
258 SETARGFILE(Type.FLOW,
259 "Sets the current value of the argfilename. Inserted before argfilecontents.",
260 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
262 UNSETARGFILE(Type.FLOW,
263 "Unsets the current value of the argfilename. Inserted after argfile contents.",
264 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
266 // these last two have no purpose in the normal Jalview application but are
267 // used by jalview.bin.Launcher to set memory settings. They are not used by
268 // argparser but are here for Usage statement reasons.
269 JVMMEMPC(Type.CONFIG,
270 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
271 + "The equals sign (\"=\") separator must be used with no spaces.",
272 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
273 JVMMEMMAX(Type.CONFIG,
274 "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"
275 + "The equals sign (\"=\") separator must be used with no spaces.",
276 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
280 public static enum Opt
282 BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
283 // false. A default can be given with setOptions(bool, Opt....).
284 // Use ArgParser.isSet(Arg) to see if this arg was not specified.
285 STRING, // This Arg can accept a value either through --arg=value or --arg
287 UNARY, // This Arg is a boolean value, true if present, false if not. Like
288 // BOOLEAN but without the --noarg option.
289 MULTI, // This Arg can be specified multiple times. Multiple values are
290 // stored in the ArgValuesMap (along with their positional index) for
292 LINKED, // This Arg can be linked to others through a --arg[linkedId] or
293 // --arg[linkedId]=value. If no linkedId is specified then the
294 // current default linkedId will be used.
295 NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
296 // first value will be used and subsequent values ignored
298 BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
299 // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
300 // Args and they cannot be linked or contain SubVals. See
301 // jalview.bin.argparser.BootstrapArgs.
302 GLOB, // This Arg can expand wildcard filename "globs" (e.g.
303 // path/*/filename*). If the Arg value is given as --arg filename*
304 // then the shell will have expanded the glob already, but if
305 // specified as --arg=filename* then the Java glob expansion method
306 // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
307 // might be different from the shell expansion rules.
308 NOACTION, // This Arg does not perform a data task, usually used to control
309 // flow in ArgParser.parse(args).
310 ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
311 // SubVals and values.
312 PRIVATE, // This Arg is used internally, and cannot be specified by the
314 SECRET, // This Arg is used by development processes and although it can be
315 // set by the user, it is not displayed to the user.
316 ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
318 INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
319 // linkedId is used, the defaultLinkedIdCounter is
320 // incremented *first*.
321 INPUT, // This Arg counts as an input for REQUIREINPUT
322 REQUIREINPUT, // This Arg can only be applied via --all if there is an
323 // input (i.e. --open or --append)
324 OUTPUT, // This Arg provides an output filename. With Opt.ALLOWALL *.ext is
325 // shorthand for --all --output={basename}.ext
326 STORED, // This Arg resets and creates a new set of "opened" linkedIds
327 HELP, // This Arg is a --help type arg
328 PRIMARY, // This Arg is the main Arg for its type
329 HASTYPE, // This Arg can have an Arg.Type assigned to it (and no value)
330 FIRST, // Move this arg to the first in usage statement (within type)
331 LAST, // Move this arg to the end in usage statement (within type)
334 public static enum Type
336 // Type restricts argument to certain usage output
338 CONFIG("arguments used to configure "
339 + ChannelProperties.getProperty("app_name") + " from startup"),
340 OPENING("arguments used to open and format alignments"),
341 STRUCTURE("arguments used to add and format 3D structure data"),
342 PROCESS("arguments used to process an alignment once opened"),
343 OUTPUT("arguments used to save data from a processed alignment"),
344 IMAGE("arguments used to export an image of an alignment"),
345 FLOW("arguments that control processing of the other arguments"), //
346 ALL("all arguments"), // mostly just a place-holder for --help-all
347 NONE, // mostly a place-holder for --help
350 private String description;
357 private Type(String description)
359 this.description = description;
362 public String description()
368 private final String[] argNames;
370 private Opt[] argOptions;
372 private boolean defaultBoolValue;
374 private String description;
378 private Arg(Type type, String description, Opt... options)
380 this(type, null, description, false, options);
383 private Arg(Type type, String description, boolean defaultBoolean,
386 this(type, null, description, defaultBoolean, options);
389 private Arg(Type type, String alternativeName, String description,
392 this(type, alternativeName, description, false, options);
395 private Arg(Type type, String alternativeName, String description,
396 boolean defaultBoolean, Opt... options)
398 this.argNames = alternativeName != null
400 { this.getName(), alternativeName }
404 this.description = description;
405 this.defaultBoolValue = defaultBoolean;
406 this.setOptions(options);
409 public String argString()
411 return argString(false);
414 public String negateArgString()
416 return argString(true);
419 private String argString(boolean negate)
421 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
422 if (negate && hasOption(Opt.BOOLEAN))
423 sb.append(ArgParser.NEGATESTRING);
424 sb.append(getName());
425 return sb.toString();
428 public String toLongString()
430 StringBuilder sb = new StringBuilder();
431 sb.append(this.getClass().getName()).append('.').append(this.name());
433 if (getNames().length > 0)
435 sb.append(String.join("\", \"", getNames()));
436 if (getNames().length > 0)
439 sb.append("\nType: " + type.name());
440 sb.append("\nOpt: ");
441 // map List<Opt> to List<String> for the String.join
442 List<String> optList = Arrays.asList(argOptions).stream()
443 .map(opt -> opt.name()).collect(Collectors.toList());
444 sb.append(String.join(", ", optList));
446 return sb.toString();
449 public String[] getNames()
454 public String getName()
456 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
460 public final String toString()
465 public boolean hasOption(Opt o)
467 if (argOptions == null)
469 for (Opt option : argOptions)
477 public boolean hasAllOptions(Opt... opts)
481 if (!this.hasOption(o))
487 protected void setOptions(Opt... options)
489 this.argOptions = options;
492 protected boolean getDefaultBoolValue()
494 return defaultBoolValue;
497 public Type getType()
502 protected String getDescription()
507 public static String booleanArgString(Arg a)
509 StringBuilder sb = new StringBuilder(a.argString());
510 if (a.hasOption(Opt.BOOLEAN))
513 sb.append(a.negateArgString());
515 return sb.toString();
518 public static final String usage()
523 public static final void appendUsageGeneral(StringBuilder sb,
526 for (Type t : EnumSet.allOf(Type.class))
528 if (t.description() != null)
530 StringBuilder argSb = new StringBuilder();
531 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
532 .append(t.name().toLowerCase(Locale.ROOT));
533 appendArgAndDescription(sb, argSb.toString(),
534 "Help for " + t.description(), null, maxArgLength);
535 sb.append(System.lineSeparator());
540 public static final String usage(List<Type> types)
542 StringBuilder sb = new StringBuilder();
544 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
545 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
546 + "] file(s)] [args]");
547 sb.append(System.lineSeparator());
548 sb.append(System.lineSeparator());
550 if (types == null || types.contains(null))
552 // always show --help
553 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
555 sb.append(System.lineSeparator());
557 appendUsageGeneral(sb, DESCRIPTIONINDENT);
561 List<Arg> args = argsSortedForDisplay(types);
564 * just use a set maxArgLength of DESCRIPTIONINDENT
566 int maxArgLength = 0;
569 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
572 String argS = argDisplayString(a);
573 if (argS.length() > maxArgLength)
574 maxArgLength = argS.length();
577 int maxArgLength = DESCRIPTIONINDENT;
579 // always show --help
580 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
581 sb.append(System.lineSeparator());
583 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
585 appendUsageGeneral(sb, maxArgLength);
588 Iterator<Arg> argsI = args.iterator();
589 Type typeSection = null;
590 while (argsI.hasNext())
592 Arg a = argsI.next();
594 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
600 if (a.getType() != typeSection)
602 typeSection = a.getType();
603 String typeDescription = a.getType().description();
604 if (typeDescription != null && typeDescription.length() > 0)
606 // typeDescription = typeDescription.substring(0,
607 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
608 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
609 sb.append(typeDescription);
610 sb.append(System.lineSeparator());
611 sb.append(System.lineSeparator());
615 appendArgUsage(sb, a, maxArgLength);
619 sb.append(System.lineSeparator());
623 return sb.toString();
626 private static void appendArgUsage(StringBuilder sb, Arg a,
629 boolean first = appendArgAndDescription(sb, null, null, a,
631 List<String> options = new ArrayList<>();
633 if (a.hasOption(Opt.BOOLEAN))
635 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
636 : a.negateArgString()));
639 if (a.hasOption(Opt.MULTI))
641 options.add("multiple");
644 if (a.hasOption(Opt.LINKED))
646 options.add("can be linked");
649 if (a.hasOption(Opt.GLOB))
651 options.add("allows file globs");
654 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
656 options.add("allows substitutions");
659 if (a.hasOption(Opt.ALLOWALL))
661 options.add("can be applied to all linked arguments");
664 if (a.hasOption(Opt.PRIVATE))
666 options.add("for internal use only");
669 if (a.hasOption(Opt.SECRET))
671 options.add("for development use only");
674 if (options.size() > 0)
678 sb.append(ARGDESCRIPTIONSEPARATOR);
682 sb.append(String.format("%-"
683 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
687 sb.append(String.join("; ", options));
689 sb.append(System.lineSeparator());
693 public static String argDisplayString(Arg a)
695 StringBuilder argSb = new StringBuilder();
697 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
698 if (a.hasOption(Opt.STRING))
699 argSb.append("=value");
700 return argSb.toString();
703 public static boolean appendArgAndDescription(StringBuilder sb,
704 String aString, String description, Arg a, int maxArgLength)
706 return appendArgAndDescription(sb, aString, description, a,
707 maxArgLength, Platform.consoleWidth());
710 public static boolean appendArgAndDescription(StringBuilder sb,
711 String aString, String description, Arg a, int maxArgLength,
714 if (aString == null && a != null)
716 aString = argDisplayString(a);
718 if (description == null && a != null)
720 description = a.getDescription();
722 sb.append(String.format("%-" + maxArgLength + "s", aString));
723 if (aString.length() > maxArgLength)
725 sb.append(System.lineSeparator());
726 sb.append(String.format("%-" + maxArgLength + "s", ""));
729 int descLength = maxLength - maxArgLength
730 - ARGDESCRIPTIONSEPARATOR.length();
731 // reformat the descriptions lines to the right width
732 Iterator<String> descLines = null;
733 if (description != null)
735 descLines = Arrays.stream(description.split("\\n")).iterator();
737 List<String> splitDescLinesList = new ArrayList<>();
738 while (descLines != null && descLines.hasNext())
740 String line = descLines.next();
741 while (line.length() > descLength)
743 int splitIndex = line.lastIndexOf(" ", descLength);
744 splitDescLinesList.add(line.substring(0, splitIndex));
745 line = line.substring(splitIndex + 1);
747 splitDescLinesList.add(line);
750 Iterator<String> splitDescLines = splitDescLinesList.iterator();
751 boolean first = true;
752 if (splitDescLines != null)
754 while (splitDescLines.hasNext())
758 sb.append(ARGDESCRIPTIONSEPARATOR);
762 sb.append(String.format("%-"
763 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
766 sb.append(splitDescLines.next());
767 sb.append(System.lineSeparator());
774 protected static Iterator<Arg> getAllOfType(Type type)
776 return getAllOfType(type, new Opt[] {});
779 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
781 Opt[] opts = options == null ? new Opt[] {} : options;
782 return EnumSet.allOf(Arg.class).stream().filter(a -> {
783 if (a.getType() != type)
794 private static List<Arg> argsSortedForDisplay(List<Type> types)
796 List<Arg> argsToSort;
797 // if no types provided, do all
798 if (types == null || types.size() == 0 || types.contains(Type.ALL))
801 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
805 argsToSort = new ArrayList<>();
806 for (Type type : types)
810 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
814 Collections.sort(argsToSort, new ArgDisplayComparator());
818 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
820 private static final int DESCRIPTIONINDENT = 20;
824 class ArgDisplayComparator implements Comparator<Arg>
826 private int compareArgOpts(Arg a, Arg b, Opt o)
828 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
829 : (b.hasOption(o) ? 1 : 0);
833 private int compareForDisplay(Arg a, Arg b)
837 // first compare types (in enum order)
838 int i = a.getType().compareTo(b.getType());
841 // do Opt.LAST next (oddly). Reversed args important!
842 i = compareArgOpts(b, a, Opt.LAST);
846 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
848 for (Opt o : optOrder)
850 i = compareArgOpts(a, b, o);
854 // finally order of appearance in enum declarations
855 return a.compareTo(b);
859 public int compare(Arg a, Arg b)
861 return compareForDisplay(a, b);