1 package jalview.bin.argparser;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.EnumSet;
6 import java.util.Iterator;
8 import java.util.Locale;
9 import java.util.stream.Collectors;
11 import jalview.bin.Cache;
12 import jalview.util.ChannelProperties;
17 // Initialising arguments (BOOTSTRAP)
18 HELP("h", "Display this help statement", Opt.UNARY, Opt.BOOTSTRAP),
20 "Display the version of "
21 + ChannelProperties.getProperty("app_name"),
22 Opt.UNARY, Opt.BOOTSTRAP),
24 "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed.",
25 Opt.UNARY, Opt.BOOTSTRAP),
26 JABAWS("Set a different URL to connect to a JABAWS server.", Opt.STRING,
28 NEWS("Show (or don't show) the news feed.", true, Opt.BOOLEAN,
30 SPLASH("Show (or don't show) the About Jalview splash screen.", true,
31 Opt.BOOLEAN, Opt.BOOTSTRAP),
33 "Show (or don't show) the questionnaire if one is available.",
34 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
35 USAGESTATS("Send (or don't send) initial launch usage stats.", true,
36 Opt.BOOLEAN, Opt.BOOTSTRAP),
38 "Attempt (or don't attempt) to connect to JABAWS web services.",
39 true, Opt.BOOLEAN, Opt.BOOTSTRAP),
40 PROPS("Use a file as the preferences file instead of the usual ~/"
41 + ChannelProperties.getProperty("preferences.filename")
42 + " file.", Opt.STRING, Opt.BOOTSTRAP),
43 DEBUG("d", "Start Jalview in debug log level.", Opt.BOOLEAN,
45 TRACE("Start Jalview in trace log level.", Opt.BOOLEAN, Opt.BOOTSTRAP,
48 "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
49 Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP),
51 "Set ‑‑substitutions to be initially enabled (or initially disabled).",
52 true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION),
54 // Opening an alignment
55 OPEN("Opens one or more alignment files or URLs in new alignment windows.",
56 Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI,
57 Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT),
58 APPEND("Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
59 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
60 Opt.ALLOWSUBSTITUTIONS, Opt.INPUT),
61 TITLE("Specifies the title for the open alignment window as string.",
62 Opt.STRING, Opt.LINKED),
63 COLOUR("Applies the colour scheme to the open alignment window. Valid values are:\n"
64 + "clustal,\n" + "blosum62,\n" + "pc-identity,\n" + "zappo,\n"
65 + "taylor,\n" + "gecos-flower,\n" + "gecos-blossom,\n"
66 + "gecos-sunset,\n" + "gecos-ocean,\n" + "hydrophobic,\n"
67 + "helix-propensity,\n" + "strand-propensity,\n"
68 + "turn-propensity,\n" + "buried-index,\n" + "nucleotide,\n"
69 + "nucleotide-ambiguity,\n" + "purine-pyrimidine,\n"
70 + "rna-helices,\n" + "t-coffee-scores,\n" + "sequence-id.",
71 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
72 FEATURES("Add a feature file or URL to the open alignment.", Opt.STRING,
73 Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
74 TREE("Add a tree file or URL to the open alignment.", Opt.STRING,
75 Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
77 "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
78 true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWALL),
79 ANNOTATIONS("Add an annotations file or URL to the open alignment.",
80 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
82 "Enforces showing (or not showing) alignment annotations.",
83 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
84 WRAP("Enforces wrapped (or not wrapped) alignment formatting.",
85 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
87 "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
88 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
90 // Adding a 3D structure
92 "Load a structure file or URL associated with a sequence in the open alignment.\n"
93 + "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.",
94 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
95 SEQID("Specify the sequence name for the preceding --structure to be associated with.",
96 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
97 PAEMATRIX("Add a PAE json matrix file to the preceding --structure.",
98 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS),
99 TEMPFAC("Set the type of temperature factor. Possible values are:\n"
100 + "default,\n" + "plddt.", Opt.STRING, Opt.LINKED),
102 "Set the structure viewer to use to open the 3d structure file specified in previous --structure to name. Possible values of name are:\n"
103 + "none,\n" + "jmol,\n" + "chimera,\n" + "chimerax,\n"
105 Opt.STRING, Opt.LINKED, Opt.MULTI),
107 "Do not show the temperature factor annotation for the preceding --structure.",
108 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL, Opt.SECRET), // keep this secret
111 SHOWSSANNOTATIONS(null, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
114 IMAGE("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"
115 + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
116 Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL,
117 Opt.REQUIREINPUT, Opt.OUTPUT),
118 TYPE("Set the image format for the preceding --image to name. Valid values for name are: svg,\n"
119 + "png,\n" + "eps,\n" + "html,\n" + "biojs.", Opt.STRING,
120 Opt.LINKED, Opt.ALLOWALL),
122 "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"
123 + "text,\n" + "lineart.",
124 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
125 SCALE("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).",
126 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
127 WIDTH("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).",
128 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
129 HEIGHT("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).",
130 Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
131 OUTPUT("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"
132 + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
133 + "stockholm (sto, stk),\n" + "pir (pir),\n" + "blc (blc),\n"
134 + "amsa (amsa),\n" + "json (json),\n" + "pileup (pileup),\n"
135 + "msf (msf),\n" + "clustal (aln),\n" + "phylip (phy),\n"
136 + "jalview (jvp, jar).", Opt.STRING, Opt.LINKED,
137 Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWALL, Opt.REQUIREINPUT,
139 FORMAT("Sets the format for the preceding --output file. Valid formats are:\n"
140 + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n" + "blc,\n"
141 + "amsa,\n" + "json,\n" + "pileup,\n" + "msf,\n" + "clustal,\n"
142 + "phylip,\n" + "jalview.", Opt.STRING, Opt.LINKED, Opt.ALLOWALL),
143 GROOVY("Process a groovy script in the file for the open alignment.",
144 Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS,
146 BACKUPS("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.",
147 true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
149 "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.",
150 Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL),
151 CLOSE("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.",
152 Opt.UNARY, Opt.LINKED, Opt.ALLOWALL),
154 // controlling flow of arguments
155 NEW("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.",
156 Opt.UNARY, Opt.MULTI, Opt.NOACTION, Opt.INCREMENTDEFAULTCOUNTER),
158 "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"
159 + "{dirname}, - the directory (folder) name of the currently --opened file (or first --appended file),\n"
160 + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
161 + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
162 + "{n} - the value of the index counter (starting at 0).\n"
163 + "{++n} - increase and substitute the value of the index counter,\n"
164 + "{} - the value of the current alignment window default index.",
165 true, Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
166 ARGFILE("Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
167 + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
168 + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
169 Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
170 Opt.ALLOWSUBSTITUTIONS),
172 "Increase the index counter used in argument value substitutions.",
173 Opt.UNARY, Opt.MULTI, Opt.NOACTION),
174 ALL("Apply the following output arguments to all sets of linked arguments.",
175 Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION),
176 QUIT("After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
181 "Allow specific stdout information. For testing purposes only.",
182 Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
183 SETPROP("Set an individual Java System property.", Opt.STRING, Opt.MULTI,
184 Opt.BOOTSTRAP, Opt.SECRET), // not in use yet
185 NIL("This argument does nothing on its own, but can be used with linkedIds.",
186 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION, Opt.SECRET),
188 // private options (inserted during arg processing)
190 "Sets the current value of the argfilename. Inserted before argfilecontents.",
191 Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTI, Opt.PRIVATE,
194 "Unsets the current value of the argfilename. Inserted after argfile contents.",
195 Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION),
197 // these last two have no purpose in the normal Jalview application but are
198 // used by jalview.bin.Launcher to set memory settings. They are not used by
199 // argparser but are here for Usage statement reasons.
201 "Only available with standalone executable jar or jalview.bin.Launcher.\n"
202 + "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
203 + "The equals sign (\"=\") separator must be used with no spaces.",
204 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
206 "Only available with standalone executable jar or jalview.bin.Launcher.\n"
207 + "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"
208 + "The equals sign (\"=\") separator must be used with no spaces.",
209 Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING),
213 public static enum Opt
215 BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
216 // false. A default can be given with setOptions(bool, Opt....).
217 // Use ArgParser.isSet(Arg) to see if this arg was not specified.
218 STRING, // This Arg can accept a value either through --arg=value or --arg
220 UNARY, // This Arg is a boolean value, true if present, false if not. Like
221 // BOOLEAN but without the --noarg option.
222 MULTI, // This Arg can be specified multiple times. Multiple values are
223 // stored in the ArgValuesMap (along with their positional index) for
225 LINKED, // This Arg can be linked to others through a --arg[linkedId] or
226 // --arg[linkedId]=value. If no linkedId is specified then the
227 // current default linkedId will be used.
228 NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
229 // first value will be used and subsequent values ignored
231 BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
232 // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
233 // Args and they cannot be linked or contain SubVals. See
234 // jalview.bin.argparser.BootstrapArgs.
235 GLOB, // This Arg can expand wildcard filename "globs" (e.g.
236 // path/*/filename*). If the Arg value is given as --arg filename*
237 // then the shell will have expanded the glob already, but if
238 // specified as --arg=filename* then the Java glob expansion method
239 // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
240 // might be different from the shell expansion rules.
241 NOACTION, // This Arg does not perform a data task, usually used to control
242 // flow in ArgParser.parse(args).
243 ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
244 // SubVals and values.
245 PRIVATE, // This Arg is used internally, and cannot be specified by the
247 SECRET, // This Arg is used by development processes and although it can be
248 // set by the user, it is not displayed to the user.
249 ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
251 INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
252 // linkedId is used, the defaultLinkedIdCounter is
253 // incremented *first*.
254 INPUT, // This Arg counts as an input for REQUIREINPUT
255 REQUIREINPUT, // This Arg can only be applied via --all if there is an
256 // input (i.e. --open or --append)
257 OUTPUT, // This Arg provides an output filename. With Opt.ALLOWALL *.ext is
258 // shorthand for --all --output={basename}.ext
261 private final String[] argNames;
263 private Opt[] argOptions;
265 private boolean defaultBoolValue = false;
267 private String description = null;
269 private Arg(String description, Opt... options)
271 this(null, description, false, options);
274 private Arg(String description, boolean defaultBoolean, Opt... options)
276 this(null, description, defaultBoolean, options);
279 private Arg(String alternativeName, String description, Opt... options)
281 this(alternativeName, description, false, options);
284 private Arg(String alternativeName, String description,
285 boolean defaultBoolean, Opt... options)
287 this.argNames = alternativeName != null
289 { this.getName(), alternativeName }
292 this.description = description;
293 this.defaultBoolValue = defaultBoolean;
294 this.setOptions(options);
297 public String argString()
299 return argString(false);
302 public String negateArgString()
304 return argString(true);
307 private String argString(boolean negate)
309 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
310 if (negate && hasOption(Opt.BOOLEAN))
311 sb.append(ArgParser.NEGATESTRING);
312 sb.append(getName());
313 return sb.toString();
316 public String toLongString()
318 StringBuilder sb = new StringBuilder();
319 sb.append(this.getClass().getName()).append('.').append(this.name());
321 if (getNames().length > 0)
323 sb.append(String.join("\", \"", getNames()));
324 if (getNames().length > 0)
327 sb.append("\nOpt: ");
328 // map List<Opt> to List<String> for the String.join
329 List<String> optList = Arrays.asList(argOptions).stream()
330 .map(opt -> opt.name()).collect(Collectors.toList());
331 sb.append(String.join(", ", optList));
333 return sb.toString();
336 public String[] getNames()
341 public String getName()
343 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
347 public final String toString()
352 public boolean hasOption(Opt o)
354 if (argOptions == null)
356 for (Opt option : argOptions)
364 protected void setOptions(Opt... options)
366 this.argOptions = options;
369 protected boolean getDefaultBoolValue()
371 return defaultBoolValue;
374 protected String getDescription()
379 public static String booleanArgString(Arg a)
381 StringBuilder sb = new StringBuilder(a.argString());
382 if (a.hasOption(Opt.BOOLEAN))
385 sb.append(a.negateArgString());
387 return sb.toString();
390 public static final String usage()
392 StringBuilder sb = new StringBuilder();
394 sb.append(ChannelProperties.getProperty("app_name"));
395 String version = Cache.getDefault("VERSION", null);
398 sb.append(" version ");
399 sb.append(Cache.getDefault("VERSION", "unknown"));
401 sb.append(System.lineSeparator());
402 sb.append("Usage: jalview [files...] [args]");
403 sb.append(System.lineSeparator());
404 sb.append(System.lineSeparator());
406 int maxArgLength = 0;
407 for (Arg a : EnumSet.allOf(Arg.class))
409 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
412 StringBuilder argSb = new StringBuilder();
413 argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
415 if (a.hasOption(Opt.STRING))
416 argSb.append("=value");
417 if (argSb.length() > maxArgLength)
418 maxArgLength = argSb.length();
421 // might want to sort these
422 for (Arg a : EnumSet.allOf(Arg.class))
424 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET))
426 StringBuilder argSb = new StringBuilder();
427 argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
429 if (a.hasOption(Opt.STRING))
430 argSb.append("=value");
431 Iterator<String> descLines = null;
432 if (a.getDescription() != null)
434 descLines = Arrays.stream(a.getDescription().split("\\n"))
437 sb.append(String.format("%-" + maxArgLength + "s", argSb.toString()));
438 boolean first = true;
439 if (descLines != null)
441 while (descLines.hasNext())
446 sb.append(" ".repeat(maxArgLength + 3));
447 sb.append(descLines.next());
448 sb.append(System.lineSeparator());
453 List<String> options = new ArrayList<>();
455 if (a.hasOption(Opt.BOOLEAN))
457 options.add("default " + (a.getDefaultBoolValue() ? a.argString()
458 : a.negateArgString()));
461 if (a.hasOption(Opt.MULTI))
463 options.add("multiple");
466 if (a.hasOption(Opt.LINKED))
468 options.add("can be linked");
471 if (a.hasOption(Opt.GLOB))
473 options.add("allows file globs");
476 if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
478 options.add("allows substitutions");
481 if (a.hasOption(Opt.ALLOWALL))
483 options.add("can be applied to all linked arguments");
486 if (a.hasOption(Opt.PRIVATE))
488 options.add("for internal use only");
491 if (a.hasOption(Opt.SECRET))
493 options.add("for development use only");
496 if (options.size() > 0)
501 sb.append(" ".repeat(maxArgLength + 3));
503 sb.append(String.join("; ", options));
505 sb.append(System.lineSeparator());
507 sb.append(System.lineSeparator());
509 return sb.toString();