JAL-629 More NG arguments: --features, --annotations, --sortbytree, --tree, --groovy...
[jalview.git] / src / jalview / bin / argparser / Arg.java
1 package jalview.bin.argparser;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.EnumSet;
6 import java.util.List;
7 import java.util.Locale;
8 import java.util.stream.Collectors;
9
10 public enum Arg
11 {
12   HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, SHOWANNOTATIONS,
13   COLOUR("color"), FEATURES, ANNOTATIONS, GROOVY, GROUPS, HEADLESS, JABAWS,
14   DISPLAY, NEWS, SORTBYTREE, USAGESTATS, APPEND, OPEN, PROPS, QUESTIONNAIRE,
15   SETPROP, TREE, VDOC, VSESS, OUTPUT, SSANNOTATIONS, NOTEMPFAC, TEMPFAC,
16   TITLE, PAEMATRIX, WRAP, NOSTRUCTURE, STRUCTURE, STRUCTUREVIEWER, IMAGE,
17   TYPE, FORMAT, OVERWRITE, RENDERER, QUIT, CLOSE, DEBUG("d"), TRACE,
18   QUIET("q"), ARGFILE, NEW, NPP("n++"), SUBSTITUTIONS, INITSUBSTITUTIONS,
19   NIL, SPLASH, SETARGFILE, UNSETARGFILE, WEBSERVICEDISCOVERY, ALL, BACKUPS,
20   TESTOUTPUT;
21
22   public static enum Opt
23   {
24     BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or
25              // false. A default can be given with setOptions(bool, Opt....).
26              // Use ArgParser.isSet(Arg) to see if this arg was not specified.
27     STRING, // This Arg can accept a value either through --arg=value or --arg
28             // value.
29     UNARY, // This Arg is a boolean value, true if present, false if not. Like
30            // BOOLEAN but without the --noarg option.
31     MULTI, // This Arg can be specified multiple times. Multiple values are
32            // stored in the ArgValuesMap (along with their positional index) for
33            // each linkedId.
34     LINKED, // This Arg can be linked to others through a --arg[linkedId] or
35             // --arg[linkedId]=value. If no linkedId is specified then the
36             // current default linkedId will be used.
37     NODUPLICATEVALUES, // This Arg can only have one value (per linkedId). The
38                        // first value will be used and subsequent values ignored
39                        // with a warning.
40     BOOTSTRAP, // This Arg value(s) can be determined at an earlier stage than
41                // non-BOOTSTRAP Args. Substitutions do not happen in BOOTSTRAP
42                // Args and they cannot be linked or contain SubVals. See
43                // jalview.bin.argparser.BootstrapArgs.
44     GLOB, // This Arg can expand wildcard filename "globs" (e.g.
45           // path/*/filename*). If the Arg value is given as --arg filename*
46           // then the shell will have expanded the glob already, but if
47           // specified as --arg=filename* then the Java glob expansion method
48           // will be used (see FileUtils.getFilenamesFromGlob()). Note that this
49           // might be different from the shell expansion rules.
50     NOACTION, // This Arg does not perform a data task, usually used to control
51               // flow in ArgParser.parse(args).
52     ALLOWSUBSTITUTIONS, // This Arg allows substitutions in its linkedId,
53                         // SubVals and values.
54     PRIVATE, // This Arg is used internally, and cannot be specified by the
55              // user.
56     ALLOWALL, // This Arg can use the '*' linkedId to apply to all known
57               // linkedIds
58     INCREMENTDEFAULTCOUNTER, // If an Arg has this option and the default
59                              // linkedId is used, the defaultLinkedIdCounter is
60                              // incremented *first*.
61     INPUT, // This Arg counts as an input for REQUIREINPUT
62     REQUIREINPUT, // This Arg can only be applied via --all if there is an
63                   // input (i.e. --open or --append)
64   }
65
66   static
67   {
68     HELP.setOptions("Display this help message", Opt.UNARY, Opt.BOOTSTRAP);
69     CALCULATION.setOptions(true, Opt.BOOLEAN); // default "true" implies only
70                                                // expecting "--nocalculation"
71     MENUBAR.setOptions(true, Opt.BOOLEAN);
72     STATUS.setOptions(true, Opt.BOOLEAN);
73     SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED);
74     SHOWANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED);
75     COLOUR.setOptions(Opt.STRING, Opt.LINKED);
76     FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
77             Opt.ALLOWSUBSTITUTIONS);
78     ANNOTATIONS.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
79             Opt.ALLOWSUBSTITUTIONS);
80     TREE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
81             Opt.ALLOWSUBSTITUTIONS);
82     GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
83             Opt.ALLOWSUBSTITUTIONS);
84     GROUPS.setOptions(Opt.STRING, Opt.LINKED);
85     HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP);
86     TESTOUTPUT.setOptions(Opt.UNARY, Opt.BOOTSTRAP);
87     JABAWS.setOptions(Opt.STRING, Opt.BOOTSTRAP);
88     DISPLAY.setOptions(true, Opt.BOOLEAN);
89     NEWS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
90     SPLASH.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
91     SORTBYTREE.setOptions(true, Opt.LINKED, Opt.BOOLEAN);
92     QUESTIONNAIRE.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
93     USAGESTATS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
94     WEBSERVICEDISCOVERY.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
95     APPEND.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
96             Opt.ALLOWSUBSTITUTIONS, Opt.INPUT);
97     OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER,
98             Opt.MULTI, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT);
99     PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP);
100     SETPROP.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP);
101
102     VDOC.setOptions(Opt.UNARY);
103     VSESS.setOptions(Opt.UNARY);
104
105     OUTPUT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS,
106             Opt.ALLOWALL, Opt.REQUIREINPUT);
107
108     SSANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED);
109     NOTEMPFAC.setOptions(Opt.UNARY, Opt.LINKED);
110     TEMPFAC.setOptions(Opt.STRING, Opt.LINKED);
111     TITLE.setOptions(Opt.STRING, Opt.LINKED);
112     PAEMATRIX.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
113             Opt.ALLOWSUBSTITUTIONS);
114     NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED);
115     STRUCTURE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
116             Opt.ALLOWSUBSTITUTIONS);
117     STRUCTUREVIEWER.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
118     WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED);
119     IMAGE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS,
120             Opt.ALLOWALL, Opt.REQUIREINPUT);
121     TYPE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
122     FORMAT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
123     RENDERER.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
124     QUIT.setOptions(Opt.UNARY);
125     OVERWRITE.setOptions(Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL);
126     BACKUPS.setOptions(true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL);
127     CLOSE.setOptions(Opt.UNARY, Opt.LINKED, Opt.ALLOWALL);
128     DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
129     TRACE.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
130     QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP);
131     ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
132             Opt.ALLOWSUBSTITUTIONS);
133     NEW.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION,
134             Opt.INCREMENTDEFAULTCOUNTER);
135     NPP.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
136     SUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
137     INITSUBSTITUTIONS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP,
138             Opt.NOACTION); // defaulting substitutions to true
139     NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION);
140     SETARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
141     UNSETARGFILE.setOptions(Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
142     ALL.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
143
144   }
145
146   private final String[] argNames;
147
148   private Opt[] argOptions;
149
150   private boolean defaultBoolValue = false;
151
152   private String description = null;
153
154   private Arg()
155   {
156     this(new String[0]);
157   }
158
159   private Arg(String... names)
160   {
161     int length = (names == null || names.length == 0
162             || (names.length == 1 && names[0] == null)) ? 1
163                     : names.length + 1;
164     this.argNames = new String[length];
165     this.argNames[0] = this.getName();
166     if (length > 1)
167       System.arraycopy(names, 0, this.argNames, 1, names.length);
168   }
169
170   public String argString()
171   {
172     return argString(false);
173   }
174
175   public String negateArgString()
176   {
177     return argString(true);
178   }
179
180   private String argString(boolean negate)
181   {
182     StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
183     if (negate && hasOption(Opt.BOOLEAN))
184       sb.append(ArgParser.NEGATESTRING);
185     sb.append(getName());
186     return sb.toString();
187   }
188
189   public String toLongString()
190   {
191     StringBuilder sb = new StringBuilder();
192     sb.append(this.getClass().getName()).append('.').append(this.name());
193     sb.append('(');
194     if (getNames().length > 0)
195       sb.append('"');
196     sb.append(String.join("\", \"", getNames()));
197     if (getNames().length > 0)
198       sb.append('"');
199     sb.append(")\n");
200     sb.append("\nOpt: ");
201     // map List<Opt> to List<String> for the String.join
202     List<String> optList = Arrays.asList(argOptions).stream()
203             .map(opt -> opt.name()).collect(Collectors.toList());
204     sb.append(String.join(", ", optList));
205     sb.append("\n");
206     return sb.toString();
207   }
208
209   public String[] getNames()
210   {
211     return argNames;
212   }
213
214   public String getName()
215   {
216     return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
217   }
218
219   @Override
220   public final String toString()
221   {
222     return getName();
223   }
224
225   public boolean hasOption(Opt o)
226   {
227     if (argOptions == null)
228       return false;
229     for (Opt option : argOptions)
230     {
231       if (o == option)
232         return true;
233     }
234     return false;
235   }
236
237   protected void setOptions(Opt... options)
238   {
239     setOptions("", false, options);
240   }
241
242   protected void setOptions(String desc, Opt... options)
243   {
244     setOptions(desc, false, options);
245   }
246
247   protected void setOptions(boolean defaultBoolValue, Opt... options)
248   {
249     setOptions("", defaultBoolValue, options);
250   }
251
252   protected void setOptions(String desc, boolean defaultBoolValue,
253           Opt... options)
254   {
255     this.description = desc;
256     this.defaultBoolValue = defaultBoolValue;
257     this.argOptions = options;
258   }
259
260   protected boolean getDefaultBoolValue()
261   {
262     return defaultBoolValue;
263   }
264
265   private void setDescription(String d)
266   {
267     description = d;
268   }
269
270   protected String getDescription()
271   {
272     return description;
273   }
274
275   public static String booleanArgString(Arg a)
276   {
277     StringBuilder sb = new StringBuilder(a.argString());
278     if (a.hasOption(Opt.BOOLEAN))
279     {
280       sb.append('/');
281       sb.append(a.negateArgString());
282     }
283     return sb.toString();
284   }
285
286   public static final String usage()
287   {
288     StringBuilder sb = new StringBuilder();
289
290     sb.append("Usage: jalview [args]");
291     sb.append(System.lineSeparator());
292
293     int maxArgLength = 0;
294     for (Arg a : EnumSet.allOf(Arg.class))
295     {
296       if (a.hasOption(Opt.PRIVATE))
297         continue;
298       StringBuilder argSb = new StringBuilder();
299       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
300               : a.argString());
301       if (a.hasOption(Opt.STRING))
302         argSb.append("=value");
303       if (argSb.length() > maxArgLength)
304         maxArgLength = argSb.length();
305     }
306
307     // might want to sort these
308     for (Arg a : EnumSet.allOf(Arg.class))
309     {
310       if (a.hasOption(Opt.PRIVATE))
311         continue;
312       StringBuilder argSb = new StringBuilder();
313       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
314               : a.argString());
315       if (a.hasOption(Opt.STRING))
316         argSb.append("=value");
317       sb.append(String.format("%-" + maxArgLength + "s  - %s",
318               argSb.toString(), a.getDescription()));
319
320       List<String> options = new ArrayList<>();
321
322       if (a.hasOption(Opt.BOOLEAN))
323       {
324         options.add("default " + (a.getDefaultBoolValue() ? a.argString()
325                 : a.negateArgString()));
326       }
327
328       if (a.hasOption(Opt.MULTI))
329       {
330         options.add("multiple");
331       }
332
333       if (a.hasOption(Opt.LINKED))
334       {
335         options.add("can be linked");
336       }
337
338       if (a.hasOption(Opt.GLOB))
339       {
340         options.add("allows file globs");
341       }
342
343       if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
344       {
345         options.add("allows substitutions");
346       }
347
348       if (options.size() > 0)
349       {
350         sb.append(" (");
351         sb.append(String.join("; ", options));
352         sb.append(')');
353       }
354       sb.append(System.lineSeparator());
355     }
356     return sb.toString();
357   }
358 }