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 STRUCTUREIMAGE(Type.STRUCTURE,
132 "Export an image of a 3D structure opened in JMOL", Opt.STRING,
133 Opt.LINKED, Opt.MULTI),
134 NOTEMPFAC(Type.STRUCTURE,
135 "Do not show the temperature factor annotation for the preceding --structure.",
136 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.OUTPUT, Opt.PRIMARY),
149 "Set the image format for the preceding --image to name. Valid values for name are: svg,\n"
150 + "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 for name 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),
166 "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"
167 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
168 + "stockholm (sto, stk),\n" + "pir (pir),\n"
169 + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
170 + "pileup (pileup),\n" + "msf (msf),\n"
171 + "clustal (aln),\n" + "phylip (phy),\n"
172 + "jalview (jvp, jar).",
173 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
174 Opt.REQUIREINPUT, Opt.OUTPUT, Opt.PRIMARY),
176 "Sets the format for the preceding --output file. Valid formats are:\n"
177 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
178 + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
179 + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
180 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
182 "Process a groovy script in the file for the open alignment.",
183 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
186 "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.",
187 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
188 OVERWRITE(Type.OUTPUT,
189 "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.",
190 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
192 "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.",
193 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
195 // controlling flow of arguments
197 "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.",
198 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
199 SUBSTITUTIONS(Type.FLOW,
200 "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
201 + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
202 + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
203 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
204 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
205 + "{n} - the value of the index counter (starting at 0).\n"
206 + "{++n} - increase and substitute the value of the index counter,\n"
207 + "{} - the value of the current alignment window default index.",
208 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
210 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
211 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
212 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
213 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
214 Opt.ALLOWSUBSTITUTIONS),
215 NPP(Type.FLOW, "n++",
216 "Increase the index counter used in argument value substitutions.",
217 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
219 "Apply the following output arguments to all sets of linked arguments.",
220 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
222 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
223 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
225 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
229 TESTOUTPUT(Type.CONFIG,
230 "Allow specific stdout information. For testing purposes only.",
231 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
232 SETPROP(Type.CONFIG, "Set an individual Java System property.",
233 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
235 "This argument does nothing on its own, but can be used with linkedIds.",
236 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
238 // private options (inserted during arg processing)
239 SETARGFILE(Type.FLOW,
240 "Sets the current value of the argfilename. Inserted before argfilecontents.",
241 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
243 UNSETARGFILE(Type.FLOW,
244 "Unsets the current value of the argfilename. Inserted after argfile contents.",
245 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
247 // these last two have no purpose in the normal Jalview application but are
248 // used by jalview.bin.Launcher to set memory settings. They are not used by
249 // argparser but are here for Usage statement reasons.
250 JVMMEMPC(Type.CONFIG,
251 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
252 + "The equals sign (\"=\") separator must be used with no spaces.",
253 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
254 JVMMEMMAX(Type.CONFIG,
255 "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"
256 + "The equals sign (\"=\") separator must be used with no spaces.",
257 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
261 public static enum Opt
263 BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
264 // false. A default can be given with setOptions(bool, Opt....).
265 // Use ArgParser.isSet(Arg) to see if this arg was not specified.
266 STRING, // This Arg can accept a value either through --arg=value or --arg
268 UNARY, // This Arg is a boolean value, true if present, false if not. Like
269 // BOOLEAN but without the --noarg option.
270 MULTI, // This Arg can be specified multiple times. Multiple values are
271 // stored in the ArgValuesMap (along with their positional index) for
273 LINKED, // This Arg can be linked to others through a --arg[linkedId] or
274 // --arg[linkedId]=value. If no linkedId is specified then the
275 // current default linkedId will be used.
276 NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
277 // first value will be used and subsequent values ignored
279 BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
280 // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
281 // Args and they cannot be linked or contain SubVals. See
282 // jalview.bin.argparser.BootstrapArgs.
283 GLOB, // This Arg can expand wildcard filename "globs" (e.g.
284 // path/*/filename*). If the Arg value is given as --arg filename*
285 // then the shell will have expanded the glob already, but if
286 // specified as --arg=filename* then the Java glob expansion method
287 // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
288 // might be different from the shell expansion rules.
289 NOACTION, // This Arg does not perform a data task, usually used to control
290 // flow in ArgParser.parse(args).
291 ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
292 // SubVals and values.
293 PRIVATE, // This Arg is used internally, and cannot be specified by the
295 SECRET, // This Arg is used by development processes and although it can be
296 // set by the user, it is not displayed to the user.
297 ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
299 INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
300 // linkedId is used, the defaultLinkedIdCounter is
301 // incremented *first*.
302 INPUT, // This Arg counts as an input for REQUIREINPUT
303 REQUIREINPUT, // This Arg can only be applied via --all if there is an
304 // input (i.e. --open or --append)
305 OUTPUT, // This Arg provides an output filename. With Opt.ALLOWALL *.ext is
306 // shorthand for --all --output={basename}.ext
307 STORED, // This Arg resets and creates a new set of "opened" linkedIds
308 HELP, // This Arg is a --help type arg
309 PRIMARY, // This Arg is the main Arg for its type
310 HASTYPE, // This Arg can have an Arg.Type assigned to it (and no value)
311 FIRST, // Move this arg to the first in usage statement (within type)
312 LAST, // Move this arg to the end in usage statement (within type)
315 public static enum Type
317 // Type restricts argument to certain usage output
319 CONFIG("arguments used to configure "
320 + ChannelProperties.getProperty("app_name") + " from startup"),
321 OPENING("arguments used to open and format alignments"),
322 STRUCTURE("arguments used to add and format 3D structure data"),
323 PROCESS("arguments used to process an alignment once opened"),
324 OUTPUT("arguments used to save data from a processed alignment"),
325 IMAGE("arguments used to export an image of an alignment"),
326 FLOW("arguments that control processing of the other arguments"), //
327 ALL("all arguments"), // mostly just a place-holder for --help-all
328 NONE, // mostly a place-holder for --help
331 private String description;
338 private Type(String description)
340 this.description = description;
343 public String description()
349 private final String[] argNames;
351 private Opt[] argOptions;
353 private boolean defaultBoolValue;
355 private String description;
359 private Arg(Type type, String description, Opt... options)
361 this(type, null, description, false, options);
364 private Arg(Type type, String description, boolean defaultBoolean,
367 this(type, null, description, defaultBoolean, options);
370 private Arg(Type type, String alternativeName, String description,
373 this(type, alternativeName, description, false, options);
376 private Arg(Type type, String alternativeName, String description,
377 boolean defaultBoolean, Opt... options)
379 this.argNames = alternativeName != null
381 { this.getName(), alternativeName }
385 this.description = description;
386 this.defaultBoolValue = defaultBoolean;
387 this.setOptions(options);
390 public String argString()
392 return argString(false);
395 public String negateArgString()
397 return argString(true);
400 private String argString(boolean negate)
402 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
403 if (negate && hasOption(Opt.BOOLEAN))
404 sb.append(ArgParser.NEGATESTRING);
405 sb.append(getName());
406 return sb.toString();
409 public String toLongString()
411 StringBuilder sb = new StringBuilder();
412 sb.append(this.getClass().getName()).append('.').append(this.name());
414 if (getNames().length > 0)
416 sb.append(String.join("\", \"", getNames()));
417 if (getNames().length > 0)
420 sb.append("\nType: " + type.name());
421 sb.append("\nOpt: ");
422 // map List<Opt> to List<String> for the String.join
423 List<String> optList = Arrays.asList(argOptions).stream()
424 .map(opt -> opt.name()).collect(Collectors.toList());
425 sb.append(String.join(", ", optList));
427 return sb.toString();
430 public String[] getNames()
435 public String getName()
437 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
441 public final String toString()
446 public boolean hasOption(Opt o)
448 if (argOptions == null)
450 for (Opt option : argOptions)
458 public boolean hasAllOptions(Opt... opts)
462 if (!this.hasOption(o))
468 protected void setOptions(Opt... options)
470 this.argOptions = options;
473 protected boolean getDefaultBoolValue()
475 return defaultBoolValue;
478 public Type getType()
483 protected String getDescription()
488 public static String booleanArgString(Arg a)
490 StringBuilder sb = new StringBuilder(a.argString());
491 if (a.hasOption(Opt.BOOLEAN))
494 sb.append(a.negateArgString());
496 return sb.toString();
499 public static final String usage()
504 public static final void appendUsageGeneral(StringBuilder sb,
507 for (Type t : EnumSet.allOf(Type.class))
509 if (t.description() != null)
511 StringBuilder argSb = new StringBuilder();
512 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
513 .append(t.name().toLowerCase(Locale.ROOT));
514 appendArgAndDescription(sb, argSb.toString(),
515 "Help for " + t.description(), null, maxArgLength);
516 sb.append(System.lineSeparator());
521 public static final String usage(List<Type> types)
523 StringBuilder sb = new StringBuilder();
525 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
526 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
527 + "] file(s)] [args]");
528 sb.append(System.lineSeparator());
529 sb.append(System.lineSeparator());
531 if (types == null || types.contains(null))
533 // always show --help
534 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
536 sb.append(System.lineSeparator());
538 appendUsageGeneral(sb, DESCRIPTIONINDENT);
542 List<Arg> args = argsSortedForDisplay(types);
545 * just use a set maxArgLength of DESCRIPTIONINDENT
547 int maxArgLength = 0;
550 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
553 String argS = argDisplayString(a);
554 if (argS.length() > maxArgLength)
555 maxArgLength = argS.length();
558 int maxArgLength = DESCRIPTIONINDENT;
560 // always show --help
561 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
562 sb.append(System.lineSeparator());
564 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
566 appendUsageGeneral(sb, maxArgLength);
569 Iterator<Arg> argsI = args.iterator();
570 Type typeSection = null;
571 while (argsI.hasNext())
573 Arg a = argsI.next();
575 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
581 if (a.getType() != typeSection)
583 typeSection = a.getType();
584 String typeDescription = a.getType().description();
585 if (typeDescription != null && typeDescription.length() > 0)
587 // typeDescription = typeDescription.substring(0,
588 // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
589 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
590 sb.append(typeDescription);
591 sb.append(System.lineSeparator());
592 sb.append(System.lineSeparator());
596 appendArgUsage(sb, a, maxArgLength);
600 sb.append(System.lineSeparator());
604 return sb.toString();
607 private static void appendArgUsage(StringBuilder sb, Arg a,
610 boolean first = appendArgAndDescription(sb, null, null, a,
612 List<String> options = new ArrayList<>();
614 if (a.hasOption(Opt.BOOLEAN))
616 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
617 : a.negateArgString()));
620 if (a.hasOption(Opt.MULTI))
622 options.add("multiple");
625 if (a.hasOption(Opt.LINKED))
627 options.add("can be linked");
630 if (a.hasOption(Opt.GLOB))
632 options.add("allows file globs");
635 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
637 options.add("allows substitutions");
640 if (a.hasOption(Opt.ALLOWALL))
642 options.add("can be applied to all linked arguments");
645 if (a.hasOption(Opt.PRIVATE))
647 options.add("for internal use only");
650 if (a.hasOption(Opt.SECRET))
652 options.add("for development use only");
655 if (options.size() > 0)
659 sb.append(ARGDESCRIPTIONSEPARATOR);
663 sb.append(String.format("%-"
664 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
668 sb.append(String.join("; ", options));
670 sb.append(System.lineSeparator());
674 public static String argDisplayString(Arg a)
676 StringBuilder argSb = new StringBuilder();
678 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
679 if (a.hasOption(Opt.STRING))
680 argSb.append("=value");
681 return argSb.toString();
684 public static boolean appendArgAndDescription(StringBuilder sb,
685 String aString, String description, Arg a, int maxArgLength)
687 return appendArgAndDescription(sb, aString, description, a,
688 maxArgLength, Platform.consoleWidth());
691 public static boolean appendArgAndDescription(StringBuilder sb,
692 String aString, String description, Arg a, int maxArgLength,
695 if (aString == null && a != null)
697 aString = argDisplayString(a);
699 if (description == null && a != null)
701 description = a.getDescription();
703 sb.append(String.format("%-" + maxArgLength + "s", aString));
704 if (aString.length() > maxArgLength)
706 sb.append(System.lineSeparator());
707 sb.append(String.format("%-" + maxArgLength + "s", ""));
710 int descLength = maxLength - maxArgLength
711 - ARGDESCRIPTIONSEPARATOR.length();
712 // reformat the descriptions lines to the right width
713 Iterator<String> descLines = null;
714 if (description != null)
716 descLines = Arrays.stream(description.split("\\n")).iterator();
718 List<String> splitDescLinesList = new ArrayList<>();
719 while (descLines != null && descLines.hasNext())
721 String line = descLines.next();
722 while (line.length() > descLength)
724 int splitIndex = line.lastIndexOf(" ", descLength);
725 splitDescLinesList.add(line.substring(0, splitIndex));
726 line = line.substring(splitIndex + 1);
728 splitDescLinesList.add(line);
731 Iterator<String> splitDescLines = splitDescLinesList.iterator();
732 boolean first = true;
733 if (splitDescLines != null)
735 while (splitDescLines.hasNext())
739 sb.append(ARGDESCRIPTIONSEPARATOR);
743 sb.append(String.format("%-"
744 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
747 sb.append(splitDescLines.next());
748 sb.append(System.lineSeparator());
755 protected static Iterator<Arg> getAllOfType(Type type)
757 return getAllOfType(type, new Opt[] {});
760 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
762 Opt[] opts = options == null ? new Opt[] {} : options;
763 return EnumSet.allOf(Arg.class).stream().filter(a -> {
764 if (a.getType() != type)
775 private static List<Arg> argsSortedForDisplay(List<Type> types)
777 List<Arg> argsToSort;
778 // if no types provided, do all
779 if (types == null || types.size() == 0 || types.contains(Type.ALL))
782 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
786 argsToSort = new ArrayList<>();
787 for (Type type : types)
791 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
795 Collections.sort(argsToSort, new ArgDisplayComparator());
799 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
801 private static final int DESCRIPTIONINDENT = 20;
805 class ArgDisplayComparator implements Comparator<Arg>
807 private int compareArgOpts(Arg a, Arg b, Opt o)
809 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
810 : (b.hasOption(o) ? 1 : 0);
814 private int compareForDisplay(Arg a, Arg b)
818 // first compare types (in enum order)
819 int i = a.getType().compareTo(b.getType());
822 // do Opt.LAST next (oddly). Reversed args important!
823 i = compareArgOpts(b, a, Opt.LAST);
827 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
829 for (Opt o : optOrder)
831 i = compareArgOpts(a, b, o);
835 // finally order of appearance in enum declarations
836 return a.compareTo(b);
840 public int compare(Arg a, Arg b)
842 return compareForDisplay(a, b);