Clover icon

Coverage Report

  1. Project Clover database Mon Dec 1 2025 15:35:32 GMT
  2. Package jalview.bin.argparser

File Arg.java

 

Coverage histogram

../../../img/srcFileCovDistChart3.png
53% of files have more coverage

Code metrics

106
232
47
4
1,116
872
113
0.49
4.94
11.75
2.4

Classes

Class Line # Actions
Arg 40 209 97
0.230088523%
Arg.Opt 360 3 3
0.3333333433.3%
Arg.Type 509 3 3
0.666666766.7%
ArgDisplayComparator 1077 17 10
0.00%
 

Contributing tests

This file is covered by 140 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.bin.argparser;
22   
23    import java.util.ArrayList;
24    import java.util.Arrays;
25    import java.util.Collections;
26    import java.util.Comparator;
27    import java.util.EnumSet;
28    import java.util.HashSet;
29    import java.util.Iterator;
30    import java.util.List;
31    import java.util.Locale;
32    import java.util.Set;
33    import java.util.stream.Collectors;
34   
35    import jalview.bin.argparser.Arg.Opt;
36    import jalview.gui.StructureViewer.ViewerType;
37    import jalview.util.ChannelProperties;
38    import jalview.util.Platform;
39   
 
40    public enum Arg
41    {
42   
43    // Initialising arguments (BOOTSTRAP)
44    HELP(Type.HELP, "h", "Display basic help", Opt.UNARY, Opt.BOOTSTRAP,
45    Opt.HASTYPE, Opt.MULTIVALUE),
46    /*
47    * Other --help-type Args will be added by the static block.
48    */
49    VERSION(Type.CONFIG, "v",
50    "Display the version of "
51    + ChannelProperties.getProperty("app_name"),
52    Opt.UNARY, Opt.BOOTSTRAP),
53    HEADLESS(Type.CONFIG,
54    "Run Jalview in headless mode. No GUI interface will be created and Jalview will quit after all arguments have been processed. "
55    + "Headless mode is assumed if an output file is to be generated, this can be overridden with --gui.",
56    Opt.UNARY, Opt.BOOTSTRAP),
57    GUI(Type.CONFIG,
58    "Do not run Jalview in headless mode. This overrides the assumption of headless mode when an output file is to be generated.",
59    Opt.UNARY, Opt.BOOTSTRAP),
60    JABAWS(Type.CONFIG, "Set a different URL to connect to a JABAWS server.",
61    Opt.STRING, Opt.BOOTSTRAP),
62    NEWS(Type.CONFIG, "Show (or don't show) the news feed.", true,
63    Opt.BOOLEAN, Opt.BOOTSTRAP),
64    SPLASH(Type.CONFIG,
65    "Show (or don't show) the About Jalview splash screen.", true,
66    Opt.BOOLEAN, Opt.BOOTSTRAP),
67    QUESTIONNAIRE(Type.CONFIG,
68    "Show (or don't show) the questionnaire if one is available.",
69    true, Opt.BOOLEAN, Opt.BOOTSTRAP),
70    JAVACONSOLE(Type.CONFIG, "Show (or don't show) the Java Console.", false,
71    Opt.BOOLEAN, Opt.BOOTSTRAP),
72    NOUSAGESTATS(Type.CONFIG, "Don't send initial launch usage stats.",
73    Opt.UNARY, Opt.BOOTSTRAP),
74    NOSTARTUPFILE(Type.CONFIG, "Don't show the default startup file.",
75    Opt.UNARY, Opt.BOOTSTRAP),
76    WEBSERVICEDISCOVERY(Type.CONFIG,
77    "Attempt (or don't attempt) to connect to JABAWS web services.",
78    true, Opt.BOOLEAN, Opt.BOOTSTRAP),
79    PROPS(Type.CONFIG,
80    "Use a file as the preferences file instead of the usual ~/"
81    + ChannelProperties.getProperty("preferences.filename")
82    + " file.",
83    Opt.STRING, Opt.BOOTSTRAP),
84    DEBUG(Type.CONFIG, "d", "Start Jalview in debug log level.", Opt.BOOLEAN,
85    Opt.BOOTSTRAP),
86    TRACE(Type.CONFIG, "Start Jalview in trace log level.", Opt.BOOLEAN,
87    Opt.BOOTSTRAP, Opt.SECRET),
88    QUIET(Type.CONFIG, "q",
89    "Stop all output to STDOUT (after the Java Virtual Machine has started). Use ‑‑quiet a second time to stop all output to STDERR.",
90    Opt.UNARY, Opt.MULTIVALUE, Opt.BOOTSTRAP),
91    INITSUBSTITUTIONS(Type.CONFIG,
92    "Set ‑‑substitutions to be initially enabled (or initially disabled).",
93    true, Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION, Opt.SECRET),
94    P(Type.CONFIG, "Set a Jalview preference value for this session.",
95    Opt.PREFIXKEV, Opt.PRESERVECASE, Opt.STRING, Opt.BOOTSTRAP,
96    Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET), // keep this secret for
97    // now.
98   
99    // Opening an alignment
100    OPEN(Type.OPENING,
101    "Opens one or more alignment files or URLs in new alignment windows.",
102    Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER,
103    Opt.MULTIVALUE, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT,
104    Opt.STORED, Opt.PRIMARY),
105    APPEND(Type.OPENING,
106    "Appends one or more alignment files or URLs to the open alignment window (or opens a new alignment if none already open).",
107    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.GLOB,
108    Opt.ALLOWSUBSTITUTIONS, Opt.INPUT, Opt.PRIMARY),
109    TITLE(Type.OPENING,
110    "Specifies the title for the open alignment window as string.",
111    Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
112    COLOUR(Type.OPENING, "color", // being a bit soft on the Americans!
113    "Applies the colour scheme to the open alignment window. Valid values include:\n"
114    + "clustal,\n" + "blosum62,\n" + "pc-identity,\n"
115    + "zappo,\n" + "taylor,\n" + "gecos-flower,\n"
116    + "gecos-blossom,\n" + "gecos-sunset,\n"
117    + "gecos-ocean,\n" + "hydrophobic,\n"
118    + "helix-propensity,\n" + "strand-propensity,\n"
119    + "turn-propensity,\n" + "buried-index,\n"
120    + "nucleotide,\n" + "nucleotide-ambiguity,\n"
121    + "purine-pyrimidine,\n" + "rna-helices,\n"
122    + "t-coffee-scores,\n" + "sequence-id.\n" + "\n"
123    + "Names of user defined colourschemes will also work,\n"
124    + "and jalview colourscheme specifications like\n"
125    + "--colour=\"D,E=red; K,R,H=0022FF; C,c=yellow\"",
126    Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
127    FEATURES(Type.OPENING, "Add a feature file or URL to the open alignment.",
128    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
129    Opt.ALLOWMULTIID),
130    TREE(Type.OPENING, "Add a tree file or URL to the open alignment.",
131    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
132    Opt.ALLOWMULTIID),
133    SORTBYTREE(Type.OPENING,
134    "Enforces sorting (or not sorting) the open alignment in the order of an attached phylogenetic tree.",
135    true, Opt.LINKED, Opt.BOOLEAN, Opt.ALLOWMULTIID),
136    ANNOTATIONS(Type.OPENING,
137    "Add an annotations file or URL to the open alignment.",
138    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
139    Opt.ALLOWMULTIID),
140    SHOWANNOTATIONS(Type.OPENING,
141    "Enforces showing (or not showing) alignment annotations.",
142    Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
143    WRAP(Type.OPENING,
144    "Enforces wrapped (or not wrapped) alignment formatting.",
145    Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
146    NOSTRUCTURE(Type.OPENING,
147    "Do not open or process any 3D structure in the ‑‑open or ‑‑append files.",
148    Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.ALLOWMULTIID),
149   
150    // Adding a 3D structure
151    STRUCTURE(Type.STRUCTURE,
152    "Load a structure file or URL associated with a sequence in the open alignment.\n"
153    + "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.",
154    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
155    Opt.PRIMARY, Opt.ALLOWMULTIID),
156    SEQID(Type.STRUCTURE,
157    "Specify the sequence name for the preceding --structure to be associated with.",
158    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
159    Opt.ALLOWMULTIID),
160    STRUCTUREVIEWER(Type.STRUCTURE,
161    "Set the structure viewer to use to open the 3D structure file specified in previous --structure to name. Possible values are:\n"
162    + "none,\n"
163    + EnumSet.allOf(ViewerType.class).stream()
164    .map(Enum::toString)
165    .collect(Collectors.joining(",\n"))
166    + ".",
167    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
168    PAEMATRIX(Type.STRUCTURE,
169    "Add a PAE json matrix file to the preceding --structure.",
170    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
171    Opt.ALLOWMULTIID),
172    TEMPFAC(Type.STRUCTURE,
173    "Set the type of temperature factor. Possible values are:\n"
174    + "default,\n" + "plddt.",
175    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
176    NOTEMPFAC(Type.STRUCTURE,
177    "Do not show the temperature factor annotation for the preceding --structure.",
178    Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID, Opt.SECRET), // keep this
179    // secret until
180    // it works!
181    SHOWSSANNOTATIONS(Type.STRUCTURE,
182    "Show/do not show the secondary structure annotations for the preceding --structure",
183    Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
184   
185    VIEWERID(Type.STRUCTURE,
186    "Specify the id of the structure viewer window to open this structure on (or create a new viewer with this id if it doesn't already exist).",
187    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
188    Opt.ALLOWMULTIID),
189    ALLSTRUCTURESVIEWERID(Type.STRUCTURE,
190    "Specify the id of the structure viewer window to open ALL structures for this MSA on. This will override use of --viewerid.",
191    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
192    Opt.ALLOWMULTIID),
193    SUPERPOSE(Type.STRUCTURE,
194    "If this structure is being added to another viewer, --superpose specifies if the structure should be aligned to existing structures in the viewer (defaults to user preference or true).",
195    true, Opt.BOOLEAN, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
196    STRUCTURESFILE(Type.STRUCTURE,
197    "Load a list of structure files or URLs associated with a sequence in the open alignment.\n"
198    + "Each line should be in the form \n"
199    + "SEQID FILENAME\n"
200    + "where SEQID is the sequence ID of the sequence the structure is associated with and FILENAME is a path or URL to a structure file.\n"
201    + "Arguments for the structure viewer, such as "
202    + Arg.STRUCTUREVIEWER.argString() + ArgParser.EQUALS
203    + ViewerType.CHIMERAX
204    + ", can be given at the top of the file before the SEQID FILENAME list.",
205   
206    /**
207    * TODO
208    * + "SEQID [options]FILENAME\n"
209    * + "where SEQID is the sequence ID of the sequence the structure is associated with, '[options]' (including square brackets) contains structure options for this structure and can be omitted completely, and FILENAME is a path or URL to a structure file.",
210    **/
211   
212    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
213    Opt.PRIMARY, Opt.ALLOWMULTIID),
214    // Outputting files
215    IMAGE(Type.IMAGE,
216    "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"
217    + "svg,\n" + "png,\n" + "eps,\n" + "html,\n" + "biojs.",
218    Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.MULTIVALUE,
219    Opt.ALLOWMULTIID, Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.PRIMARY),
220    STRUCTUREIMAGE(new Type[]
221    { Type.IMAGE, Type.STRUCTUREIMAGE },
222    "Export an image of a 3D structure opened in JMOL", Opt.STRING,
223    Opt.LINKED, Opt.MULTIVALUE, Opt.OUTPUTFILE, Opt.ALLOWMULTIID,
224    Opt.PRIMARY),
225    TYPE(Type.IMAGE,
226    "Set the image format for the preceding " + Arg.IMAGE.argString()
227    + " or " + Arg.STRUCTUREIMAGE.argString()
228    + ". Valid values are:\n" + "svg,\n" + "png,\n" + "eps,\n"
229    + "html,\n" + "biojs.",
230    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
231    TEXTRENDERER(Type.IMAGE,
232    "Sets whether text in a vector image format (SVG, HTML, EPS) should be rendered as text or vector line-art. Possible values are:\n"
233    + "text,\n" + "lineart.",
234    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
235    SCALE(Type.IMAGE,
236    "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).",
237    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
238    WIDTH(Type.IMAGE,
239    "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).",
240    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
241    HEIGHT(Type.IMAGE,
242    "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).",
243    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
244    IMAGECOLOUR(Type.IMAGE, "imagecolor", // being a bit soft on the Americans!
245    "Applies the colour scheme to the open alignment window for this image, otherwise the value of "
246    + Arg.COLOUR.argString()
247    + " (or none) will apply. Valid values are the same as "
248    + Arg.COLOUR.argString() + ".",
249    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
250    BGCOLOUR(Type.IMAGE, "bgcolor", // being a bit soft on the Americans!
251    "Applies a background colour to the structure image. Valid values are named colours known to Java or RRGGBB 6 digit hex-string.",
252    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWMULTIID),
253    OUTPUT(Type.OUTPUT,
254    "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"
255    + "fasta (fa, fasta, mfa, fastq),\n" + "pfam (pfam),\n"
256    + "stockholm (sto, stk),\n" + "pir (pir),\n"
257    + "blc (blc),\n" + "amsa (amsa),\n" + "json (json),\n"
258    + "pileup (pileup),\n" + "msf (msf),\n"
259    + "clustal (aln),\n" + "phylip (phy),\n"
260    + "jalview (jvp, jar).",
261    Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS, Opt.ALLOWMULTIID,
262    Opt.REQUIREINPUT, Opt.OUTPUTFILE, Opt.STDOUT, Opt.PRIMARY),
263    FORMAT(Type.OUTPUT,
264    "Sets the format for the preceding --output file. Valid formats are:\n"
265    + "fasta,\n" + "pfam,\n" + "stockholm,\n" + "pir,\n"
266    + "blc,\n" + "amsa,\n" + "json,\n" + "pileup,\n"
267    + "msf,\n" + "clustal,\n" + "phylip,\n" + "jalview.",
268    Opt.STRING, Opt.LINKED, Opt.ALLOWMULTIID),
269    GROOVY(Type.PROCESS,
270    "Process a groovy script in the file for the open alignment.",
271    Opt.STRING, Opt.LINKED, Opt.MULTIVALUE, Opt.ALLOWSUBSTITUTIONS,
272    Opt.ALLOWMULTIID),
273    BACKUPS(Type.OUTPUT,
274    "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.",
275    true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
276    OVERWRITE(Type.OUTPUT,
277    "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.",
278    Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWMULTIID),
279    CLOSE(Type.OPENING,
280    "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.",
281    Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
282    MKDIRS(Type.OUTPUT,
283    "Automatically create directories when outputting a file to a new directory.",
284    Opt.UNARY, Opt.LINKED, Opt.ALLOWMULTIID),
285   
286    // controlling flow of arguments
287    NEW(Type.FLOW,
288    "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.",
289    Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION,
290    Opt.INCREMENTDEFAULTCOUNTER),
291    SUBSTITUTIONS(Type.FLOW,
292    "The following argument values allow (or don't allow) subsituting filename parts. This is initially true. Valid substitutions are:\n"
293    + "{basename} - the filename-without-extension of the currently --opened file (or first --appended file),\n"
294    + "{dirname} - the directory (folder) name of the currently --opened file (or first --appended file),\n"
295    + "{argfilebasename} - the filename-without-extension of the current --argfile,\n"
296    + "{argfiledirname} - the directory (folder) name of the current --argfile,\n"
297    + "{n} - the value of the index counter (starting at 0).\n"
298    + "{++n} - increase and substitute the value of the index counter,\n"
299    + "{} - the value of the current alignment window default index.",
300    true, Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
301    ARGFILE(Type.FLOW,
302    "Open one or more files filename and read, line-by-line, as arguments to Jalview.\n"
303    + "Values in an argfile should be given with an equals sign (\"=\") separator with no spaces.\n"
304    + "Note that if you use one or more --argfile arguments then all other non-initialising arguments will be ignored.",
305    Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.GLOB,
306    Opt.ALLOWSUBSTITUTIONS),
307    NPP(Type.FLOW, "n++",
308    "Increase the index counter used in argument value substitutions.",
309    Opt.UNARY, Opt.MULTIVALUE, Opt.NOACTION),
310    ALL(Type.FLOW,
311    "Apply the following output arguments to all sets of linked arguments.",
312    Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
313    OPENED(Type.FLOW,
314    "Apply the following output arguments to all of the last --open'ed set of linked arguments.",
315    Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
316    QUIT(Type.FLOW,
317    "After all files have been opened, appended and output, quit Jalview. In ‑‑headless mode this already happens.",
318    Opt.UNARY),
319    NOQUIT(Type.FLOW,
320    "Secret arg to not quit after --headless mode for tests",
321    Opt.UNARY, Opt.SECRET),
322    ALLSTRUCTURES(Type.FLOW,
323    "Apply the following 3D structure formatting arguments to all structures within the open alignment.",
324    Opt.BOOLEAN, Opt.MULTIVALUE, Opt.NOACTION),
325   
326    // secret options
327    TESTOUTPUT(Type.CONFIG,
328    "Allow specific stdout information. For testing purposes only.",
329    Opt.UNARY, Opt.BOOTSTRAP, Opt.SECRET), // do not show this to the user
330    SETPROP(Type.CONFIG, "Set an individual Java System property.",
331    Opt.STRING, Opt.MULTIVALUE, Opt.BOOTSTRAP, Opt.SECRET), // not in use
332    // yet
333    NIL(Type.FLOW,
334    "This argument does nothing on its own, but can be used with linkedIds.",
335    Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.NOACTION, Opt.SECRET),
336   
337    // private options (inserted during arg processing)
338    SETARGFILE(Type.FLOW,
339    "Sets the current value of the argfilename. Inserted before argfilecontents.",
340    Opt.UNARY, Opt.LINKED, Opt.STRING, Opt.MULTIVALUE, Opt.PRIVATE,
341    Opt.NOACTION),
342    UNSETARGFILE(Type.FLOW,
343    "Unsets the current value of the argfilename. Inserted after argfile contents.",
344    Opt.UNARY, Opt.LINKED, Opt.MULTIVALUE, Opt.PRIVATE, Opt.NOACTION),
345   
346    // these last two have no purpose in the normal Jalview application but are
347    // used by jalview.bin.Launcher to set memory settings. They are not used by
348    // argparser but are here for Usage statement and parsing reasons.
349    JVMMEMPC(Type.CONFIG,
350    "Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected.\n"
351    + "The equals sign (\"=\") separator must be used with no spaces.",
352    Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
353    JVMMEMMAX(Type.CONFIG,
354    "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"
355    + "The equals sign (\"=\") separator must be used with no spaces.",
356    Opt.NOACTION, Opt.BOOTSTRAP, Opt.STRING, Opt.LAST),
357   
358    ;
359   
 
360    public static enum Opt
361    {
362    /*
363    * A BOOLEAN Arg can be specified as --arg or --noarg to give true or false.
364    * A default can be given with setOptions(bool, Opt....).
365    * Use ArgParser.isSet(Arg) to see if this arg was not specified.
366    */
367    BOOLEAN("can be negated with " + ArgParser.DOUBLEDASH
368    + ArgParser.NEGATESTRING + "..."),
369   
370    /*
371    * A STRING Arg will take a value either through --arg=value or --arg value.
372    */
373    STRING("expects a value"),
374    /*
375    * A UNARY Arg is a boolean value, true if present, false if not.
376    * Like BOOLEAN but without the --noarg option.
377    */
378    UNARY(null),
379    /*
380    * A MULTI Arg can be specified multiple times.
381    * Multiple values are stored in the ArgValuesMap (along with their positional index) for each linkedId.
382    */
383    MULTIVALUE("can be specified multiple times"),
384    /*
385    * A Linked Arg can be linked to others through a --arg[linkedId] or --arg[linkedId]=value.
386    * If no linkedId is specified then the current default linkedId will be used.
387    */
388    LINKED("is linked to an alignment"),
389    /*
390    * A NODUPLICATES Arg can only have one value (per linkedId).
391    * The first value will be used and subsequent values ignored with a warning.
392    */
393    NODUPLICATEVALUES("cannot have the same value more than once"),
394    /*
395    * A BOOTSTRAP Arg value(s) can be determined at an earlier stage than non-BOOTSTRAP Args.
396    * Substitutions do not happen in BOOTSTRAP Args and they cannot be linked or contain SubVals.
397    * See jalview.bin.argparser.BootstrapArgs.
398    */
399    BOOTSTRAP("a configuration argument"),
400    /*
401    * A GLOB Arg can expand wildcard filename "globs" (e.g. path/* /filename*).
402    * If the Arg value is given as --arg filename* then the shell will have expanded the glob already,
403    * but if specified as --arg=filename* then the Java glob expansion method will be used
404    * (see FileUtils.getFilenamesFromGlob()).
405    * Note that this might be different from the shell expansion rules.
406    */
407    GLOB("can take multiple filenames with wildcards"),
408    /*
409    * A NOACTION Arg does not perform a data task,
410    * usually used to control flow in ArgParser.parse(args).
411    */
412    NOACTION(null),
413    /*
414    * An ALLOWSUBSTITUTIONS Arg allows substitutions in its linkedId,
415    * SubVals and values.
416    */
417    ALLOWSUBSTITUTIONS("values can use substitutions"),
418    /*
419    * A PRIVATE Arg is used internally, and cannot be specified by the user.
420    */
421    PRIVATE(null),
422    /*
423    * A SECRET Arg is used by development processes and although it can be set by the user,
424    * it is not displayed to the user.
425    */
426    SECRET(null),
427    /*
428    * An ALLOWALL Arg can use the '*' linkedId to apply to all known linkedIds
429    */
430    ALLOWMULTIID("can be used with " + ArgParser.DOUBLEDASH + "all"),
431    /*
432    * If an Arg has the INCREMENTDEFAULTCOUNTER option and the default linkedId is used,
433    * the defaultLinkedIdCounter is incremented *first*.
434    */
435    INCREMENTDEFAULTCOUNTER("starts a new default alignment"),
436    /*
437    * An INPUT Arg counts as an input for REQUIREINPUT
438    */
439    INPUT(null),
440    /*
441    * A REQUIREINPUT Arg can only be applied via --all if there is an input
442    * (i.e. --open or --append)
443    */
444    REQUIREINPUT(null),
445    /*
446    * An OUTPUTFILE Arg provides an output filename. With Opt.ALLOWALL *.ext is shorthand for
447    * --all --output={basename}.ext
448    */
449    OUTPUTFILE("output file --headless will be assumed unless --gui used"),
450    /*
451    * A STDOUT Arg can take an output filename that can be '-' to mean print to STDOUT.
452    */
453    STDOUT("allows the output filename '" + ArgParser.STDOUTFILENAME
454    + "' to mean output to STDOUT"),
455    /*
456    * A STORED Arg resets and creates a new set of "opened" linkedIds
457    */
458    STORED(null),
459    /*
460    * A HELP Arg is a --help type arg
461    */
462    HELP("provides a help statement"),
463    /*
464    * A PRIMARY Arg is the main Arg for its type
465    */
466    PRIMARY("is a primary argument for its type"),
467    /*
468    * A HASTYPE Arg can have an Arg.Type assigned to its ArgValue
469    */
470    HASTYPE(null),
471    /*
472    * A FIRST arg gets moved to appear first in the usage statement (within type)
473    */
474    FIRST(null),
475    /*
476    * A LAST arg gets moved to appear last in the usage statement (within type)
477    */
478    LAST(null),
479    /*
480    * After other args are checked, the following args can prefix a KEY=VALUE argument
481    */
482    PREFIXKEV("prefixes key=value"),
483    /*
484    * do not lowercase the name when getting the arg name or arg string
485    */
486    PRESERVECASE(null),
487    //
488    ;
489   
490    private String description;
491   
 
492  0 toggle private Opt()
493    {
494  0 description = null;
495    }
496   
 
497  1404 toggle private Opt(String description)
498    {
499  1404 this.description = description;
500    }
501   
 
502  0 toggle public String description()
503    {
504  0 return description;
505    }
506   
507    }
508   
 
509    public static enum Type
510    {
511    // Type restricts argument to certain usage output
512    HELP, // --help
513    CONFIG("arguments used to configure "
514    + ChannelProperties.getProperty("app_name") + " from startup"),
515    OPENING("arguments used to open and format alignments"),
516    STRUCTURE("arguments used to add and format 3D structure data"),
517    PROCESS("arguments used to process an alignment once opened"),
518    OUTPUT("arguments used to save data from a processed alignment"),
519    IMAGE("arguments used to export an image of an alignment"),
520    STRUCTUREIMAGE("arguments used to export an image of an structure"),
521    FLOW("arguments that control processing of the other arguments"), //
522    ALL("all arguments"), // mostly just a place-holder for --help-all
523    NONE, // mostly a place-holder for --help
524    INVALID;
525   
526    private String description;
527   
 
528  162 toggle private Type()
529    {
530  162 description = null;
531    }
532   
 
533  486 toggle private Type(String description)
534    {
535  486 this.description = description;
536    }
537   
 
538  0 toggle public String description()
539    {
540  0 return description;
541    }
542    }
543   
544    private final String[] argNames;
545   
546    private Opt[] argOptions;
547   
548    private boolean defaultBoolValue;
549   
550    private String description;
551   
552    private Type[] types;
553   
 
554  2862 toggle private Arg(Type type, String description, Opt... options)
555    {
556  2862 this(new Type[] { type }, description, options);
557    }
558   
 
559  2916 toggle private Arg(Type[] type, String description, Opt... options)
560    {
561  2916 this(type, null, description, false, options);
562    }
563   
 
564  540 toggle private Arg(Type type, String description, boolean defaultBoolean,
565    Opt... options)
566    {
567  540 this(new Type[] { type }, description, defaultBoolean, options);
568    }
569   
 
570  540 toggle private Arg(Type[] type, String description, boolean defaultBoolean,
571    Opt... options)
572    {
573  540 this(type, null, description, defaultBoolean, options);
574    }
575   
 
576  432 toggle private Arg(Type type, String alternativeName, String description,
577    Opt... options)
578    {
579  432 this(new Type[] { type }, alternativeName, description, options);
580    }
581   
 
582  432 toggle private Arg(Type[] type, String alternativeName, String description,
583    Opt... options)
584    {
585  432 this(type, alternativeName, description, false, options);
586    }
587   
 
588  0 toggle private Arg(Type type, String alternativeName, String description,
589    boolean defaultBoolean, Opt... options)
590    {
591  0 this(new Type[] { type }, alternativeName, description, defaultBoolean,
592    options);
593    }
594   
 
595  3888 toggle private Arg(Type[] type, String alternativeName, String description,
596    boolean defaultBoolean, Opt... options)
597    {
598  3888 this.types = type;
599  3888 this.description = description;
600  3888 this.defaultBoolValue = defaultBoolean;
601  3888 this.setOptions(options);
602  3888 this.argNames = alternativeName != null
603    ? new String[]
604    { this.getName(), alternativeName }
605    : new String[]
606    { this.getName() };
607    }
608   
 
609  710 toggle public String argString()
610    {
611  710 return argString(false);
612    }
613   
 
614  4 toggle public String negateArgString()
615    {
616  4 return argString(true);
617    }
618   
 
619  714 toggle private String argString(boolean negate)
620    {
621  714 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
622  714 if (negate && hasOption(Opt.BOOLEAN))
623  4 sb.append(ArgParser.NEGATESTRING);
624  714 sb.append(getName());
625  714 return sb.toString();
626    }
627   
 
628  0 toggle public String toLongString()
629    {
630  0 StringBuilder sb = new StringBuilder();
631  0 sb.append(this.getClass().getName()).append('.').append(this.name());
632  0 sb.append('(');
633  0 if (getNames().length > 0)
634  0 sb.append('"');
635  0 sb.append(String.join("\", \"", getNames()));
636  0 if (getNames().length > 0)
637  0 sb.append('"');
638  0 sb.append(")\n");
639  0 for (Type type : getTypes())
640    {
641  0 String typeName = type.name();
642  0 sb.append("\nType: " + typeName);
643    }
644  0 sb.append("\nOpt: ");
645    // map List<Opt> to List<String> for the String.join
646  0 List<String> optList = Arrays.asList(argOptions).stream()
647    .map(opt -> opt.name()).collect(Collectors.toList());
648  0 sb.append(String.join(", ", optList));
649  0 sb.append("\n");
650  0 return sb.toString();
651    }
652   
 
653  3888 toggle public String[] getNames()
654    {
655  3888 return argNames;
656    }
657   
 
658  8326 toggle public String getName()
659    {
660  8326 String name = hasOption(Opt.PRESERVECASE) ? this.name()
661    : this.name().toLowerCase(Locale.ROOT);
662  8326 return name.replace('_', '-');
663    }
664   
 
665  15 toggle public String getNegatedName()
666    {
667  15 String name = hasOption(Opt.PRESERVECASE) ? this.name()
668    : this.name().toLowerCase(Locale.ROOT);
669  15 StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
670  15 sb.append(name);
671  15 return sb.toString().replace('_', '-');
672    }
673   
 
674  860 toggle @Override
675    public final String toString()
676    {
677  860 return getName();
678    }
679   
 
680  45504 toggle public boolean hasOption(Opt o)
681    {
682  45504 if (argOptions == null)
683  0 return false;
684  45504 for (Opt option : argOptions)
685    {
686  172983 if (o == option)
687  11583 return true;
688    }
689  33921 return false;
690    }
691   
 
692  0 toggle public boolean hasAllOptions(Opt... opts)
693    {
694  0 for (Opt o : opts)
695    {
696  0 if (!this.hasOption(o))
697  0 return false;
698    }
699  0 return true;
700    }
701   
 
702  1040 toggle protected Opt[] getOptions()
703    {
704  1040 return argOptions;
705    }
706   
 
707  3888 toggle protected void setOptions(Opt... options)
708    {
709  3888 this.argOptions = options;
710    }
711   
 
712  2083 toggle protected boolean getDefaultBoolValue()
713    {
714  2083 return defaultBoolValue;
715    }
716   
 
717  7 toggle public Type getFirstType()
718    {
719  7 return this.getTypes()[0];
720    }
721   
 
722  5602 toggle public Type[] getTypes()
723    {
724  5602 return this.types;
725    }
726   
 
727  2208 toggle public boolean sharesType(Arg a)
728    {
729  2208 return this.hasType(a.getTypes());
730    }
731   
 
732  2242 toggle public boolean hasType(Type... types)
733    {
734  2242 Set<Type> typesSet = new HashSet<>(Arrays.asList(types));
735  2242 return this.hasType(typesSet);
736    }
737   
 
738  2332 toggle public boolean hasType(Set<Type> typesSet)
739    {
740  2332 for (Type type : getTypes())
741    {
742  2381 if (typesSet.contains(type))
743    {
744  1236 return true;
745    }
746    }
747  1096 return false;
748    }
749   
 
750  0 toggle protected String getDescription()
751    {
752  0 return description;
753    }
754   
 
755  0 toggle public static String booleanArgString(Arg a)
756    {
757  0 StringBuilder sb = new StringBuilder(a.argString());
758  0 if (a.hasOption(Opt.BOOLEAN))
759    {
760  0 sb.append('/');
761  0 sb.append(a.negateArgString());
762    }
763  0 return sb.toString();
764    }
765   
 
766  0 toggle public static final String usage()
767    {
768  0 return usage(null);
769    }
770   
 
771  0 toggle public static final void appendUsageGeneral(StringBuilder sb,
772    int maxArgLength)
773    {
774  0 Set<Type> firstTypes = new HashSet<>();
775  0 for (Arg a : EnumSet.allOf(Arg.class))
776    {
777  0 if (!firstTypes.contains(a.getFirstType()))
778    {
779  0 firstTypes.add(a.getFirstType());
780    }
781    }
782  0 for (Type t : EnumSet.allOf(Type.class))
783    {
784  0 if (t.description() != null && firstTypes.contains(t))
785    {
786  0 StringBuilder argSb = new StringBuilder();
787  0 argSb.append(Arg.HELP.argString()).append(ArgParser.SINGLEDASH)
788    .append(t.name().toLowerCase(Locale.ROOT));
789  0 appendArgAndDescription(sb, argSb.toString(),
790    "Help for " + t.description(), null, maxArgLength);
791  0 sb.append(System.lineSeparator());
792    }
793    }
794    }
795   
 
796  0 toggle public static final String usage(List<Type> types)
797    {
798  0 StringBuilder sb = new StringBuilder();
799   
800  0 sb.append("usage: jalview [" + Arg.HEADLESS.argString() + "] [["
801    + Arg.OPEN.argString() + "/" + Arg.APPEND.argString()
802    + "] file(s)] [args]");
803  0 sb.append(System.lineSeparator());
804  0 sb.append(System.lineSeparator());
805   
806  0 if (types == null || types.contains(null))
807    {
808    // always show --help
809  0 appendArgAndDescription(sb, null, "Display this basic help", Arg.HELP,
810    DESCRIPTIONINDENT);
811  0 sb.append(System.lineSeparator());
812   
813  0 appendUsageGeneral(sb, DESCRIPTIONINDENT);
814    }
815    else
816    {
817  0 List<Arg> args = argsSortedForDisplay(types);
818   
819  0 int maxArgLength = DESCRIPTIONINDENT;
820   
821    // always show --help
822  0 appendArgAndDescription(sb, null, null, Arg.HELP, maxArgLength);
823  0 sb.append(System.lineSeparator());
824   
825  0 if ((args.contains(Arg.HELP) && types.contains(Type.ALL)))
826    {
827  0 appendUsageGeneral(sb, maxArgLength);
828    }
829   
830  0 Iterator<Arg> argsI = args.iterator();
831  0 Type typeSection = null;
832  0 while (argsI.hasNext())
833    {
834  0 Arg a = argsI.next();
835   
836  0 if (a.hasOption(Opt.PRIVATE) || a.hasOption(Opt.SECRET)
837    || a == Arg.HELP)
838    {
839  0 continue;
840    }
841   
842  0 if (a.getFirstType() != typeSection)
843    {
844  0 typeSection = a.getFirstType();
845  0 String typeDescription = a.getFirstType().description();
846  0 if (typeDescription != null && typeDescription.length() > 0)
847    {
848    // typeDescription = typeDescription.substring(0,
849    // 1).toUpperCase(Locale.ROOT) + typeDescription.substring(1);
850  0 typeDescription = typeDescription.toUpperCase(Locale.ROOT);
851  0 sb.append(typeDescription);
852  0 sb.append(System.lineSeparator());
853  0 sb.append(System.lineSeparator());
854    }
855    }
856   
857  0 appendArgUsage(sb, a, maxArgLength, Platform.consoleWidth());
858   
859  0 if (argsI.hasNext())
860    {
861  0 sb.append(System.lineSeparator());
862    }
863    }
864    }
865  0 return sb.toString();
866    }
867   
 
868  0 toggle private static void appendArgUsage(StringBuilder sb, Arg a,
869    int maxArgLength, int maxWidth)
870    {
871  0 boolean first = appendArgAndDescription(sb, null, null, a,
872    maxArgLength);
873  0 List<String> options = new ArrayList<>();
874   
875  0 for (Opt o : EnumSet.allOf(Opt.class))
876    {
877  0 if (a.hasOption(o) && o.description() != null)
878    {
879  0 options.add(o.description());
880    }
881    }
882   
883  0 final String optDisplaySeparator = "; ";
884  0 if (options.size() > 0)
885    {
886  0 int linelength = 0;
887  0 String spacing = String.format("%-"
888    + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
889    "");
890  0 if (first)
891    {
892  0 sb.append(ARGDESCRIPTIONSEPARATOR);
893  0 linelength += maxArgLength + ARGDESCRIPTIONSEPARATOR.length();
894    }
895    else
896    {
897  0 sb.append(spacing);
898  0 linelength += spacing.length();
899    }
900  0 if (options.size() > 0)
901    {
902  0 boolean optFirst = true;
903  0 Iterator<String> optionsI = options.listIterator();
904  0 while (optionsI.hasNext())
905    {
906  0 String desc = optionsI.next();
907  0 if (optFirst)
908    {
909  0 sb.append("(");
910  0 linelength += 1;
911    }
912  0 int descLength = desc.length()
913  0 + (optionsI.hasNext() ? optDisplaySeparator.length() : 0);
914  0 if (linelength + descLength > maxWidth)
915    {
916  0 sb.append(System.lineSeparator());
917  0 linelength = 0;
918  0 sb.append(spacing);
919  0 linelength += spacing.length();
920    }
921    // sb.append(linelength + "+" + desc.length() + " ");
922  0 sb.append(desc);
923  0 linelength += desc.length();
924  0 if (optionsI.hasNext())
925    {
926  0 sb.append(optDisplaySeparator);
927  0 linelength += optDisplaySeparator.length();
928    }
929  0 optFirst = false;
930    }
931  0 sb.append(')');
932  0 sb.append(System.lineSeparator());
933    }
934    }
935    }
936   
 
937  0 toggle public static String argDisplayString(Arg a)
938    {
939  0 StringBuilder argSb = new StringBuilder();
940  0 argSb.append(
941  0 a.hasOption(Opt.BOOLEAN) ? booleanArgString(a) : a.argString());
942  0 if (a.hasOption(Opt.STRING))
943    {
944  0 if (a.hasOption(Opt.PREFIXKEV))
945    {
946  0 argSb.append("key=value");
947    }
948    else
949    {
950  0 argSb.append("=value");
951    }
952    }
953  0 return argSb.toString();
954    }
955   
 
956  0 toggle public static boolean appendArgAndDescription(StringBuilder sb,
957    String aString, String description, Arg a, int maxArgLength)
958    {
959  0 return appendArgAndDescription(sb, aString, description, a,
960    maxArgLength, Platform.consoleWidth());
961    }
962   
 
963  0 toggle public static boolean appendArgAndDescription(StringBuilder sb,
964    String aString, String description, Arg a, int maxArgLength,
965    int maxLength)
966    {
967  0 if (aString == null && a != null)
968    {
969  0 aString = argDisplayString(a);
970    }
971  0 if (description == null && a != null)
972    {
973  0 description = a.getDescription();
974    }
975  0 sb.append(String.format("%-" + maxArgLength + "s", aString));
976  0 if (aString.length() > maxArgLength)
977    {
978  0 sb.append(System.lineSeparator());
979  0 sb.append(String.format("%-" + maxArgLength + "s", ""));
980    }
981   
982  0 int descLength = maxLength - maxArgLength
983    - ARGDESCRIPTIONSEPARATOR.length();
984    // reformat the descriptions lines to the right width
985  0 Iterator<String> descLines = null;
986  0 if (description != null)
987    {
988  0 descLines = Arrays.stream(description.split("\\n")).iterator();
989    }
990  0 List<String> splitDescLinesList = new ArrayList<>();
991  0 while (descLines != null && descLines.hasNext())
992    {
993  0 String line = descLines.next();
994  0 while (line.length() > descLength)
995    {
996  0 int splitIndex = line.lastIndexOf(" ", descLength);
997  0 splitDescLinesList.add(line.substring(0, splitIndex));
998  0 line = line.substring(splitIndex + 1);
999    }
1000  0 splitDescLinesList.add(line);
1001    }
1002   
1003  0 Iterator<String> splitDescLines = splitDescLinesList.iterator();
1004  0 boolean first = true;
1005  0 if (splitDescLines != null)
1006    {
1007  0 while (splitDescLines.hasNext())
1008    {
1009  0 if (first)
1010    {
1011  0 sb.append(ARGDESCRIPTIONSEPARATOR);
1012    }
1013    else
1014    {
1015  0 sb.append(String.format("%-"
1016    + (maxArgLength + ARGDESCRIPTIONSEPARATOR.length()) + "s",
1017    ""));
1018    }
1019  0 sb.append(splitDescLines.next());
1020  0 sb.append(System.lineSeparator());
1021  0 first = false;
1022    }
1023    }
1024  0 return first;
1025    }
1026   
 
1027  0 toggle protected static Iterator<Arg> getAllOfType(Type type)
1028    {
1029  0 return getAllOfType(type, new Opt[] {});
1030    }
1031   
 
1032  0 toggle protected static Iterator<Arg> getAllOfType(Type type, Opt... options)
1033    {
1034  0 Opt[] opts = options == null ? new Opt[] {} : options;
1035  0 return EnumSet.allOf(Arg.class).stream().filter(a -> {
1036  0 if (!a.hasType(type))
1037  0 return false;
1038  0 for (Opt o : opts)
1039    {
1040  0 if (!a.hasOption(o))
1041  0 return false;
1042    }
1043  0 return true;
1044    }).iterator();
1045    }
1046   
 
1047  0 toggle private static List<Arg> argsSortedForDisplay(List<Type> types)
1048    {
1049  0 List<Arg> argsToSort;
1050    // if no types provided, do all
1051  0 if (types == null || types.size() == 0 || types.contains(Type.ALL))
1052    {
1053  0 argsToSort = Arrays
1054    .asList(EnumSet.allOf(Arg.class).toArray(new Arg[] {}));
1055    }
1056    else
1057    {
1058  0 argsToSort = new ArrayList<>();
1059  0 for (Type type : types)
1060    {
1061  0 if (type == null)
1062  0 continue;
1063  0 Arg.getAllOfType(type).forEachRemaining(a -> argsToSort.add(a));
1064    }
1065    }
1066   
1067  0 Collections.sort(argsToSort, new ArgDisplayComparator());
1068  0 return argsToSort;
1069    }
1070   
1071    private static final String ARGDESCRIPTIONSEPARATOR = " - ";
1072   
1073    private static final int DESCRIPTIONINDENT = 20;
1074   
1075    }
1076   
 
1077    class ArgDisplayComparator implements Comparator<Arg>
1078    {
 
1079  0 toggle private int compareArgOpts(Arg a, Arg b, Opt o)
1080    {
1081  0 int i = a.hasOption(o) ? (b.hasOption(o) ? 0 : -1)
1082  0 : (b.hasOption(o) ? 1 : 0);
1083  0 return i;
1084    }
1085   
 
1086  0 toggle private int compareForDisplay(Arg a, Arg b)
1087    {
1088  0 if (b == null)
1089  0 return -1;
1090    // first compare types (in enum order)
1091  0 int i = a.getFirstType().compareTo(b.getFirstType());
1092  0 if (i != 0)
1093  0 return i;
1094    // do Opt.LAST next (oddly). Reversed args important!
1095  0 i = compareArgOpts(b, a, Opt.LAST);
1096  0 if (i != 0)
1097  0 return i;
1098    // priority order
1099  0 Opt[] optOrder = { Opt.HELP, Opt.FIRST, Opt.PRIMARY, Opt.STRING,
1100    Opt.BOOLEAN };
1101  0 for (Opt o : optOrder)
1102    {
1103  0 i = compareArgOpts(a, b, o);
1104  0 if (i != 0)
1105  0 return i;
1106    }
1107    // finally order of appearance in enum declarations
1108  0 return a.compareTo(b);
1109    }
1110   
 
1111  0 toggle @Override
1112    public int compare(Arg a, Arg b)
1113    {
1114  0 return compareForDisplay(a, b);
1115    }
1116    }