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 {basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
201 + "{dirname}, - the directory (folder) name of the currently --opened file (or first --appended file),\n"
202 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
203 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
204 + "{n} - the value of the index counter (starting at 0).\n"
205 + "{++n} - increase and substitute the value of the index counter,\n"
206 + "{} - the value of the current alignment window default index.",
207 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
209 "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
210 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
211 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
212 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
213 Opt.ALLOWSUBSTITUTIONS),
214 NPP(Type.FLOW, "n++",
215 "Increase the index counter used in argument value substitutions.",
216 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
218 "Apply the following output arguments to all sets of linked arguments.",
219 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
221 "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
222 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
224 "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
228 TESTOUTPUT(Type.CONFIG,
229 "Allow specific stdout information. For testing purposes only.",
230 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
231 SETPROP(Type.CONFIG, "Set an individual Java System property.",
232 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
234 "This argument does nothing on its own, but can be used with linkedIds.",
235 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
237 // private options (inserted during arg processing)
238 SETARGFILE(Type.FLOW,
239 "Sets the current value of the argfilename. Inserted before argfilecontents.",
240 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
242 UNSETARGFILE(Type.FLOW,
243 "Unsets the current value of the argfilename. Inserted after argfile contents.",
244 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
246 // these last two have no purpose in the normal Jalview application but are
247 // used by jalview.bin.Launcher to set memory settings. They are not used by
248 // argparser but are here for Usage statement reasons.
249 JVMMEMPC(Type.CONFIG,
250 "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
251 + "The equals sign (\"=\") separator must be used with no spaces.",
252 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
253 JVMMEMMAX(Type.CONFIG,
254 "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"
255 + "The equals sign (\"=\") separator must be used with no spaces.",
256 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
260 public static enum Opt
262 BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
263 // false. A default can be given with setOptions(bool, Opt....).
264 // Use ArgParser.isSet(Arg) to see if this arg was not specified.
265 STRING, // This Arg can accept a value either through --arg=value or --arg
267 UNARY, // This Arg is a boolean value, true if present, false if not. Like
268 // BOOLEAN but without the --noarg option.
269 MULTI, // This Arg can be specified multiple times. Multiple values are
270 // stored in the ArgValuesMap (along with their positional index) for
272 LINKED, // This Arg can be linked to others through a --arg[linkedId] or
273 // --arg[linkedId]=value. If no linkedId is specified then the
274 // current default linkedId will be used.
275 NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
276 // first value will be used and subsequent values ignored
278 BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
279 // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
280 // Args and they cannot be linked or contain SubVals. See
281 // jalview.bin.argparser.BootstrapArgs.
282 GLOB, // This Arg can expand wildcard filename "globs" (e.g.
283 // path/*/filename*). If the Arg value is given as --arg filename*
284 // then the shell will have expanded the glob already, but if
285 // specified as --arg=filename* then the Java glob expansion method
286 // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
287 // might be different from the shell expansion rules.
288 NOACTION, // This Arg does not perform a data task, usually used to control
289 // flow in ArgParser.parse(args).
290 ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
291 // SubVals and values.
292 PRIVATE, // This Arg is used internally, and cannot be specified by the
294 SECRET, // This Arg is used by development processes and although it can be
295 // set by the user, it is not displayed to the user.
296 ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
298 INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
299 // linkedId is used, the defaultLinkedIdCounter is
300 // incremented *first*.
301 INPUT, // This Arg counts as an input for REQUIREINPUT
302 REQUIREINPUT, // This Arg can only be applied via --all if there is an
303 // input (i.e. --open or --append)
304 OUTPUT, // This Arg provides an output filename. With Opt.ALLOWALL *.ext is
305 // shorthand for --all --output={basename}.ext
306 STORED, // This Arg resets and creates a new set of "opened" linkedIds
307 HELP, // This Arg is a --help type arg
308 PRIMARY, // This Arg is the main Arg for its type
309 HASTYPE, // This Arg can have an Arg.Type assigned to it (and no value)
310 FIRST, // Move this arg to the first in usage statement (within type)
311 LAST, // Move this arg to the end in usage statement (within type)
314 public static enum Type
316 // Type restricts argument to certain usage output
318 CONFIG("arguments used to configure "
319 + ChannelProperties.getProperty("app_name") + " from startup"),
320 OPENING("arguments used to open and format alignments"),
321 STRUCTURE("arguments used to add and format 3D structure data"),
322 PROCESS("arguments used to process an alignment once opened"),
323 OUTPUT("arguments used to save data from a processed alignment"),
324 IMAGE("arguments used to export an image of an alignment"),
325 FLOW("arguments that control processing of the other arguments"), //
326 ALL("all arguments"), // mostly just a place-holder for --help-all
327 NONE, // mostly a place-holder for --help
330 private String description;
337 private Type(String description)
339 this.description = description;
342 public String description()
348 private final String[] argNames;
350 private Opt[] argOptions;
352 private boolean defaultBoolValue;
354 private String description;
358 private Arg(Type type, String description, Opt... options)
360 this(type, null, description, false, options);
363 private Arg(Type type, String description, boolean defaultBoolean,
366 this(type, null, description, defaultBoolean, options);
369 private Arg(Type type, String alternativeName, String description,
372 this(type, alternativeName, description, false, options);
375 private Arg(Type type, String alternativeName, String description,
376 boolean defaultBoolean, Opt... options)
378 this.argNames = alternativeName != null
380 { this.getName(), alternativeName }
384 this.description = description;
385 this.defaultBoolValue = defaultBoolean;
386 this.setOptions(options);
389 public String argString()
391 return argString(false);
394 public String negateArgString()
396 return argString(true);
399 private String argString(boolean negate)
401 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
402 if (negate && hasOption(Opt.BOOLEAN))
403 sb.append(ArgParser.NEGATESTRING);
404 sb.append(getName());
405 return sb.toString();
408 public String toLongString()
410 StringBuilder sb = new StringBuilder();
411 sb.append(this.getClass().getName()).append('.').append(this.name());
413 if (getNames().length > 0)
415 sb.append(String.join("\", \"", getNames()));
416 if (getNames().length > 0)
419 sb.append("\nType: " + type.name());
420 sb.append("\nOpt: ");
421 // map List<Opt> to List<String> for the String.join
422 List<String> optList = Arrays.asList(argOptions).stream()
423 .map(opt -> opt.name()).collect(Collectors.toList());
424 sb.append(String.join(", ", optList));
426 return sb.toString();
429 public String[] getNames()
434 public String getName()
436 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
440 public final String toString()
445 public boolean hasOption(Opt o)
447 if (argOptions == null)
449 for (Opt option : argOptions)
457 public boolean hasAllOptions(Opt... opts)
461 if (!this.hasOption(o))
467 protected void setOptions(Opt... options)
469 this.argOptions = options;
472 protected boolean getDefaultBoolValue()
474 return defaultBoolValue;
477 public Type getType()
482 protected String getDescription()
487 public static String booleanArgString(Arg a)
489 StringBuilder sb = new StringBuilder(a.argString());
490 if (a.hasOption(Opt.BOOLEAN))
493 sb.append(a.negateArgString());
495 return sb.toString();
498 public static final String usage()
503 public static final void appendUsageGeneral(StringBuilder sb,
506 for (Type t : EnumSet.allOf(Type.class))
508 if (t.description() != null)
510 StringBuilder argSb = new StringBuilder();
511 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
512 .append(t.name().toLowerCase(Locale.ROOT));
513 appendArgAndDescription(sb, argSb.toString(),
514 "Help for " + t.description(), null, maxArgLength);
515 sb.append(System.lineSeparator());
520 public static final String usage(List<Type> types)
522 StringBuilder sb = new StringBuilder();
524 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] ["
525 + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
526 + " file(s)] [args]");
527 sb.append(System.lineSeparator());
528 sb.append(System.lineSeparator());
530 if (types == null || types.contains(null))
532 // always show --help
533 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
535 sb.append(System.lineSeparator());
537 appendUsageGeneral(sb, DESCRIPTIONINDENT);
541 List<Arg> args = argsSortedForDisplay(types);
544 * just use a set maxArgLength of DESCRIPTIONINDENT
546 int maxArgLength = 0;
549 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
552 String argS = argDisplayString(a);
553 if (argS.length() > maxArgLength)
554 maxArgLength = argS.length();
557 int maxArgLength = DESCRIPTIONINDENT;
559 // always show --help
560 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
561 sb.append(System.lineSeparator());
563 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
565 appendUsageGeneral(sb, maxArgLength);
568 Iterator<Arg> argsI = args.iterator();
569 while (argsI.hasNext())
571 Arg a = argsI.next();
573 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
579 appendArgUsage(sb, a, maxArgLength);
582 sb.append(System.lineSeparator());
585 return sb.toString();
588 private static void appendArgUsage(StringBuilder sb, Arg a,
591 boolean first = appendArgAndDescription(sb, null, null, a,
593 List<String> options = new ArrayList<>();
595 if (a.hasOption(Opt.BOOLEAN))
597 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
598 : a.negateArgString()));
601 if (a.hasOption(Opt.MULTI))
603 options.add("multiple");
606 if (a.hasOption(Opt.LINKED))
608 options.add("can be linked");
611 if (a.hasOption(Opt.GLOB))
613 options.add("allows file globs");
616 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
618 options.add("allows substitutions");
621 if (a.hasOption(Opt.ALLOWALL))
623 options.add("can be applied to all linked arguments");
626 if (a.hasOption(Opt.PRIVATE))
628 options.add("for internal use only");
631 if (a.hasOption(Opt.SECRET))
633 options.add("for development use only");
636 if (options.size() > 0)
640 sb.append(ARGDESCRIPTIONSEPARATOR);
644 sb.append(String.format("%-"
645 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
649 sb.append(String.join("; ", options));
651 sb.append(System.lineSeparator());
655 public static String argDisplayString(Arg a)
657 StringBuilder argSb = new StringBuilder();
659 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
660 if (a.hasOption(Opt.STRING))
661 argSb.append("=value");
662 return argSb.toString();
665 public static boolean appendArgAndDescription(StringBuilder sb,
666 String aString, String description, Arg a, int maxArgLength)
668 return appendArgAndDescription(sb, aString, description, a,
669 maxArgLength, Platform.consoleWidth());
672 public static boolean appendArgAndDescription(StringBuilder sb,
673 String aString, String description, Arg a, int maxArgLength,
676 if (aString == null && a != null)
678 aString = argDisplayString(a);
680 if (description == null && a != null)
682 description = a.getDescription();
684 sb.append(String.format("%-" + maxArgLength + "s", aString));
685 if (aString.length() > maxArgLength)
687 sb.append(System.lineSeparator());
688 sb.append(String.format("%-" + maxArgLength + "s", ""));
691 int descLength = maxLength - maxArgLength
692 - ARGDESCRIPTIONSEPARATOR.length();
693 // reformat the descriptions lines to the right width
694 Iterator<String> descLines = null;
695 if (description != null)
697 descLines = Arrays.stream(description.split("\\n")).iterator();
699 List<String> splitDescLinesList = new ArrayList<>();
700 while (descLines != null && descLines.hasNext())
702 String line = descLines.next();
703 while (line.length() > descLength)
705 int splitIndex = line.lastIndexOf(" ", descLength);
706 if (splitIndex > descLength)
712 splitDescLinesList.add(line.substring(0, splitIndex));
713 line = line.substring(splitIndex + 1);
716 splitDescLinesList.add(line);
719 Iterator<String> splitDescLines = splitDescLinesList.iterator();
720 boolean first = true;
721 if (splitDescLines != null)
723 while (splitDescLines.hasNext())
726 sb.append(ARGDESCRIPTIONSEPARATOR);
728 sb.append(String.format("%-"
729 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
731 sb.append(splitDescLines.next());
732 sb.append(System.lineSeparator());
739 protected static Iterator<Arg> getAllOfType(Type type)
741 return getAllOfType(type, new Opt[] {});
744 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
746 Opt[] opts = options == null ? new Opt[] {} : options;
747 return EnumSet.allOf(Arg.class).stream().filter(a -> {
748 if (a.getType() != type)
759 private static List<Arg> argsSortedForDisplay(List<Type> types)
761 List<Arg> argsToSort;
762 // if no types provided, do all
763 if (types == null || types.size() == 0 || types.contains(Type.ALL))
766 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
770 argsToSort = new ArrayList<>();
771 for (Type type : types)
775 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
779 Collections.sort(argsToSort, new ArgDisplayComparator());
783 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
785 private static final int DESCRIPTIONINDENT = 20;
789 class ArgDisplayComparator implements Comparator<Arg>
791 private int compareArgOpts(Arg a, Arg b, Opt o)
793 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
794 : (b.hasOption(o) ? 1 : 0);
798 private int compareForDisplay(Arg a, Arg b)
802 // first compare types (in enum order)
803 int i = a.getType().compareTo(b.getType());
806 // do Opt.LAST next (oddly). Reversed args important!
807 i = compareArgOpts(b, a, Opt.LAST);
811 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
813 for (Opt o : optOrder)
815 i = compareArgOpts(a, b, o);
819 // finally order of appearance in enum declarations
820 return a.compareTo(b);
824 public int compare(Arg a, Arg b)
826 return compareForDisplay(a, b);