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 while (argsI.hasNext())
572 Arg a = argsI.next();
574 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
580 appendArgUsage(sb, a, maxArgLength);
583 sb.append(System.lineSeparator());
586 return sb.toString();
589 private static void appendArgUsage(StringBuilder sb, Arg a,
592 boolean first = appendArgAndDescription(sb, null, null, a,
594 List<String> options = new ArrayList<>();
596 if (a.hasOption(Opt.BOOLEAN))
598 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
599 : a.negateArgString()));
602 if (a.hasOption(Opt.MULTI))
604 options.add("multiple");
607 if (a.hasOption(Opt.LINKED))
609 options.add("can be linked");
612 if (a.hasOption(Opt.GLOB))
614 options.add("allows file globs");
617 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
619 options.add("allows substitutions");
622 if (a.hasOption(Opt.ALLOWALL))
624 options.add("can be applied to all linked arguments");
627 if (a.hasOption(Opt.PRIVATE))
629 options.add("for internal use only");
632 if (a.hasOption(Opt.SECRET))
634 options.add("for development use only");
637 if (options.size() > 0)
641 sb.append(ARGDESCRIPTIONSEPARATOR);
645 sb.append(String.format("%-"
646 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
650 sb.append(String.join("; ", options));
652 sb.append(System.lineSeparator());
656 public static String argDisplayString(Arg a)
658 StringBuilder argSb = new StringBuilder();
660 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
661 if (a.hasOption(Opt.STRING))
662 argSb.append("=value");
663 return argSb.toString();
666 public static boolean appendArgAndDescription(StringBuilder sb,
667 String aString, String description, Arg a, int maxArgLength)
669 return appendArgAndDescription(sb, aString, description, a,
670 maxArgLength, Platform.consoleWidth());
673 public static boolean appendArgAndDescription(StringBuilder sb,
674 String aString, String description, Arg a, int maxArgLength,
677 if (aString == null && a != null)
679 aString = argDisplayString(a);
681 if (description == null && a != null)
683 description = a.getDescription();
685 sb.append(String.format("%-" + maxArgLength + "s", aString));
686 if (aString.length() > maxArgLength)
688 sb.append(System.lineSeparator());
689 sb.append(String.format("%-" + maxArgLength + "s", ""));
692 int descLength = maxLength - maxArgLength
693 - ARGDESCRIPTIONSEPARATOR.length();
694 // reformat the descriptions lines to the right width
695 Iterator<String> descLines = null;
696 if (description != null)
698 descLines = Arrays.stream(description.split("\\n")).iterator();
700 List<String> splitDescLinesList = new ArrayList<>();
701 while (descLines != null && descLines.hasNext())
703 String line = descLines.next();
704 while (line.length() > descLength)
706 int splitIndex = line.lastIndexOf(" ", descLength);
707 splitDescLinesList.add(line.substring(0, splitIndex));
708 line = line.substring(splitIndex + 1);
710 splitDescLinesList.add(line);
713 Iterator<String> splitDescLines = splitDescLinesList.iterator();
714 boolean first = true;
715 if (splitDescLines != null)
717 while (splitDescLines.hasNext())
721 sb.append(ARGDESCRIPTIONSEPARATOR);
725 sb.append(String.format("%-"
726 + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
729 sb.append(splitDescLines.next());
730 sb.append(System.lineSeparator());
737 protected static Iterator<Arg> getAllOfType(Type type)
739 return getAllOfType(type, new Opt[] {});
742 protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
744 Opt[] opts = options == null ? new Opt[] {} : options;
745 return EnumSet.allOf(Arg.class).stream().filter(a -> {
746 if (a.getType() != type)
757 private static List<Arg> argsSortedForDisplay(List<Type> types)
759 List<Arg> argsToSort;
760 // if no types provided, do all
761 if (types == null || types.size() == 0 || types.contains(Type.ALL))
764 .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
768 argsToSort = new ArrayList<>();
769 for (Type type : types)
773 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
777 Collections.sort(argsToSort, new ArgDisplayComparator());
781 private static final String ARGDESCRIPTIONSEPARATOR = " - ";
783 private static final int DESCRIPTIONINDENT = 20;
787 class ArgDisplayComparator implements Comparator<Arg>
789 private int compareArgOpts(Arg a, Arg b, Opt o)
791 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
792 : (b.hasOption(o) ? 1 : 0);
796 private int compareForDisplay(Arg a, Arg b)
800 // first compare types (in enum order)
801 int i = a.getType().compareTo(b.getType());
804 // do Opt.LAST next (oddly). Reversed args important!
805 i = compareArgOpts(b, a, Opt.LAST);
809 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
811 for (Opt o : optOrder)
813 i = compareArgOpts(a, b, o);
817 // finally order of appearance in enum declarations
818 return a.compareTo(b);
822 public int compare(Arg a, Arg b)
824 return compareForDisplay(a, b);