d77c0755618f316b1f4950d8c1b92beff6d4730b
[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, TEXTRENDERER, QUIT, CLOSE, DEBUG("d"), TRACE,
18   QUIET("q"), ARGFILE, NEW, NPP("n++"), SUBSTITUTIONS, INITSUBSTITUTIONS,
19   NIL, SPLASH, SETARGFILE, UNSETARGFILE, WEBSERVICEDISCOVERY, ALL, BACKUPS,
20   TESTOUTPUT, SEQID;
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     SEQID.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
115             Opt.ALLOWSUBSTITUTIONS);
116     NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED);
117     STRUCTURE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
118             Opt.ALLOWSUBSTITUTIONS);
119     STRUCTUREVIEWER.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
120     WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED);
121     IMAGE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS,
122             Opt.ALLOWALL, Opt.REQUIREINPUT);
123     TYPE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
124     FORMAT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
125     TEXTRENDERER.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWALL);
126     QUIT.setOptions(Opt.UNARY);
127     OVERWRITE.setOptions(Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL);
128     BACKUPS.setOptions(true, Opt.BOOLEAN, Opt.LINKED, Opt.ALLOWALL);
129     CLOSE.setOptions(Opt.UNARY, Opt.LINKED, Opt.ALLOWALL);
130     DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
131     TRACE.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
132     QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP);
133     ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
134             Opt.ALLOWSUBSTITUTIONS);
135     NEW.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION,
136             Opt.INCREMENTDEFAULTCOUNTER);
137     NPP.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
138     SUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
139     INITSUBSTITUTIONS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP,
140             Opt.NOACTION); // defaulting substitutions to true
141     NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION);
142     SETARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
143     UNSETARGFILE.setOptions(Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
144     ALL.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
145
146   }
147
148   private final String[] argNames;
149
150   private Opt[] argOptions;
151
152   private boolean defaultBoolValue = false;
153
154   private String description = null;
155
156   private Arg()
157   {
158     this(new String[0]);
159   }
160
161   private Arg(String... names)
162   {
163     int length = (names == null || names.length == 0
164             || (names.length == 1 && names[0] == null)) ? 1
165                     : names.length + 1;
166     this.argNames = new String[length];
167     this.argNames[0] = this.getName();
168     if (length > 1)
169       System.arraycopy(names, 0, this.argNames, 1, names.length);
170   }
171
172   public String argString()
173   {
174     return argString(false);
175   }
176
177   public String negateArgString()
178   {
179     return argString(true);
180   }
181
182   private String argString(boolean negate)
183   {
184     StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
185     if (negate && hasOption(Opt.BOOLEAN))
186       sb.append(ArgParser.NEGATESTRING);
187     sb.append(getName());
188     return sb.toString();
189   }
190
191   public String toLongString()
192   {
193     StringBuilder sb = new StringBuilder();
194     sb.append(this.getClass().getName()).append('.').append(this.name());
195     sb.append('(');
196     if (getNames().length > 0)
197       sb.append('"');
198     sb.append(String.join("\", \"", getNames()));
199     if (getNames().length > 0)
200       sb.append('"');
201     sb.append(")\n");
202     sb.append("\nOpt: ");
203     // map List<Opt> to List<String> for the String.join
204     List<String> optList = Arrays.asList(argOptions).stream()
205             .map(opt -> opt.name()).collect(Collectors.toList());
206     sb.append(String.join(", ", optList));
207     sb.append("\n");
208     return sb.toString();
209   }
210
211   public String[] getNames()
212   {
213     return argNames;
214   }
215
216   public String getName()
217   {
218     return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
219   }
220
221   @Override
222   public final String toString()
223   {
224     return getName();
225   }
226
227   public boolean hasOption(Opt o)
228   {
229     if (argOptions == null)
230       return false;
231     for (Opt option : argOptions)
232     {
233       if (o == option)
234         return true;
235     }
236     return false;
237   }
238
239   protected void setOptions(Opt... options)
240   {
241     setOptions("", false, options);
242   }
243
244   protected void setOptions(String desc, Opt... options)
245   {
246     setOptions(desc, false, options);
247   }
248
249   protected void setOptions(boolean defaultBoolValue, Opt... options)
250   {
251     setOptions("", defaultBoolValue, options);
252   }
253
254   protected void setOptions(String desc, boolean defaultBoolValue,
255           Opt... options)
256   {
257     this.description = desc;
258     this.defaultBoolValue = defaultBoolValue;
259     this.argOptions = options;
260   }
261
262   protected boolean getDefaultBoolValue()
263   {
264     return defaultBoolValue;
265   }
266
267   private void setDescription(String d)
268   {
269     description = d;
270   }
271
272   protected String getDescription()
273   {
274     return description;
275   }
276
277   public static String booleanArgString(Arg a)
278   {
279     StringBuilder sb = new StringBuilder(a.argString());
280     if (a.hasOption(Opt.BOOLEAN))
281     {
282       sb.append('/');
283       sb.append(a.negateArgString());
284     }
285     return sb.toString();
286   }
287
288   public static final String usage()
289   {
290     StringBuilder sb = new StringBuilder();
291
292     sb.append("Usage: jalview [args]");
293     sb.append(System.lineSeparator());
294
295     int maxArgLength = 0;
296     for (Arg a : EnumSet.allOf(Arg.class))
297     {
298       if (a.hasOption(Opt.PRIVATE))
299         continue;
300       StringBuilder argSb = new StringBuilder();
301       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
302               : a.argString());
303       if (a.hasOption(Opt.STRING))
304         argSb.append("=value");
305       if (argSb.length() > maxArgLength)
306         maxArgLength = argSb.length();
307     }
308
309     // might want to sort these
310     for (Arg a : EnumSet.allOf(Arg.class))
311     {
312       if (a.hasOption(Opt.PRIVATE))
313         continue;
314       StringBuilder argSb = new StringBuilder();
315       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
316               : a.argString());
317       if (a.hasOption(Opt.STRING))
318         argSb.append("=value");
319       sb.append(String.format("%-" + maxArgLength + "s  - %s",
320               argSb.toString(), a.getDescription()));
321
322       List<String> options = new ArrayList<>();
323
324       if (a.hasOption(Opt.BOOLEAN))
325       {
326         options.add("default " + (a.getDefaultBoolValue() ? a.argString()
327                 : a.negateArgString()));
328       }
329
330       if (a.hasOption(Opt.MULTI))
331       {
332         options.add("multiple");
333       }
334
335       if (a.hasOption(Opt.LINKED))
336       {
337         options.add("can be linked");
338       }
339
340       if (a.hasOption(Opt.GLOB))
341       {
342         options.add("allows file globs");
343       }
344
345       if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
346       {
347         options.add("allows substitutions");
348       }
349
350       if (options.size() > 0)
351       {
352         sb.append(" (");
353         sb.append(String.join("; ", options));
354         sb.append(')');
355       }
356       sb.append(System.lineSeparator());
357     }
358     return sb.toString();
359   }
360 }