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