JAL-629 Added --all arg, [*] linkedId and functionality to apply some args to all...
[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, QUIT, CLOSE,
17   DEBUG("d"), QUIET("q"), ARGFILE, NEWFRAME, NPP("n++"), SUBSTITUTIONS,
18   INITSUBSTITUTIONS, NIL, SPLASH, SETARGFILE, UNSETARGFILE,
19   WEBSERVICEDISCOVERY, ALLFRAMES;
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   }
58
59   static
60   {
61     HELP.setOptions("Display this help message", Opt.UNARY, Opt.BOOTSTRAP);
62     CALCULATION.setOptions(true, Opt.BOOLEAN); // default "true" implies only
63                                                // expecting "--nocalculation"
64     MENUBAR.setOptions(true, Opt.BOOLEAN);
65     STATUS.setOptions(true, Opt.BOOLEAN);
66     SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED);
67     ANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED);
68     COLOUR.setOptions(Opt.STRING, Opt.LINKED);
69     FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
70             Opt.ALLOWSUBSTITUTIONS);
71     GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
72             Opt.ALLOWSUBSTITUTIONS);
73     GROUPS.setOptions(Opt.STRING, Opt.LINKED);
74     HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP);
75     JABAWS.setOptions(Opt.STRING);
76     DISPLAY.setOptions(true, Opt.BOOLEAN);
77     GUI.setOptions(true, Opt.BOOLEAN);
78     NEWS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
79     SPLASH.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP);
80     // expects a string value
81     SORTBYTREE.setOptions(true, Opt.BOOLEAN);
82     USAGESTATS.setOptions(true, Opt.BOOLEAN);
83     APPEND.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
84             Opt.ALLOWSUBSTITUTIONS);
85     OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB,
86             Opt.ALLOWSUBSTITUTIONS);
87     PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP);
88     QUESTIONNAIRE.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
89     SETPROP.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP);
90     TREE.setOptions(Opt.STRING);
91
92     VDOC.setOptions(Opt.UNARY);
93     VSESS.setOptions(Opt.UNARY);
94
95     OUTPUT.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS,
96             Opt.ALLOWALL);
97
98     SSANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED);
99     NOTEMPFAC.setOptions(Opt.UNARY, Opt.LINKED);
100     TEMPFAC.setOptions(Opt.STRING, Opt.LINKED);
101     TITLE.setOptions(Opt.STRING, Opt.LINKED);
102     PAEMATRIX.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
103             Opt.ALLOWSUBSTITUTIONS);
104     NOSTRUCTURE.setOptions(Opt.UNARY, Opt.LINKED);
105     STRUCTURE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI,
106             Opt.ALLOWSUBSTITUTIONS);
107     STRUCTUREVIEWER.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
108     WRAP.setOptions(Opt.BOOLEAN, Opt.LINKED);
109     IMAGE.setOptions(Opt.STRING, Opt.LINKED, Opt.ALLOWSUBSTITUTIONS,
110             Opt.ALLOWALL);
111     QUIT.setOptions(Opt.UNARY);
112     CLOSE.setOptions(Opt.UNARY, Opt.LINKED, Opt.ALLOWALL);
113     DEBUG.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
114     QUIET.setOptions(Opt.UNARY, Opt.MULTI, Opt.BOOTSTRAP);
115     ARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP, Opt.GLOB,
116             Opt.ALLOWSUBSTITUTIONS);
117     NEWFRAME.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
118     NPP.setOptions(Opt.UNARY, Opt.MULTI, Opt.NOACTION);
119     SUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
120     INITSUBSTITUTIONS.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP, Opt.NOACTION);
121     NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION);
122     SETARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
123     UNSETARGFILE.setOptions(Opt.MULTI, Opt.PRIVATE, Opt.NOACTION);
124     WEBSERVICEDISCOVERY.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP);
125     ALLFRAMES.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION);
126   }
127
128   private final String[] argNames;
129
130   private Opt[] argOptions;
131
132   private boolean defaultBoolValue = false;
133
134   private String description = null;
135
136   private Arg()
137   {
138     this(new String[0]);
139   }
140
141   private Arg(String... names)
142   {
143     int length = (names == null || names.length == 0
144             || (names.length == 1 && names[0] == null)) ? 1
145                     : names.length + 1;
146     this.argNames = new String[length];
147     this.argNames[0] = this.getName();
148     if (length > 1)
149       System.arraycopy(names, 0, this.argNames, 1, names.length);
150   }
151
152   public String argString()
153   {
154     return argString(false);
155   }
156
157   public String negateArgString()
158   {
159     return argString(true);
160   }
161
162   private String argString(boolean negate)
163   {
164     StringBuilder sb = new StringBuilder(ArgParser.DOUBLEDASH);
165     if (negate && hasOption(Opt.BOOLEAN))
166       sb.append(ArgParser.NEGATESTRING);
167     sb.append(getName());
168     return sb.toString();
169   }
170
171   public String toLongString()
172   {
173     StringBuilder sb = new StringBuilder();
174     sb.append(this.getClass().getName()).append('.').append(this.name());
175     sb.append('(');
176     if (getNames().length > 0)
177       sb.append('"');
178     sb.append(String.join("\", \"", getNames()));
179     if (getNames().length > 0)
180       sb.append('"');
181     sb.append(")\n");
182     sb.append("\nOpt: ");
183     // map List<Opt> to List<String> for the String.join
184     List<String> optList = Arrays.asList(argOptions).stream()
185             .map(opt -> opt.name()).collect(Collectors.toList());
186     sb.append(String.join(", ", optList));
187     sb.append("\n");
188     return sb.toString();
189   }
190
191   public String[] getNames()
192   {
193     return argNames;
194   }
195
196   public String getName()
197   {
198     return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
199   }
200
201   @Override
202   public final String toString()
203   {
204     return getName();
205   }
206
207   public boolean hasOption(Opt o)
208   {
209     if (argOptions == null)
210       return false;
211     for (Opt option : argOptions)
212     {
213       if (o == option)
214         return true;
215     }
216     return false;
217   }
218
219   protected void setOptions(Opt... options)
220   {
221     setOptions("", false, options);
222   }
223
224   protected void setOptions(String desc, Opt... options)
225   {
226     setOptions(desc, false, options);
227   }
228
229   protected void setOptions(boolean defaultBoolValue, Opt... options)
230   {
231     setOptions("", defaultBoolValue, options);
232   }
233
234   protected void setOptions(String desc, boolean defaultBoolValue,
235           Opt... options)
236   {
237     this.description = desc;
238     this.defaultBoolValue = defaultBoolValue;
239     this.argOptions = options;
240   }
241
242   protected boolean getDefaultBoolValue()
243   {
244     return defaultBoolValue;
245   }
246
247   private void setDescription(String d)
248   {
249     description = d;
250   }
251
252   protected String getDescription()
253   {
254     return description;
255   }
256
257   public static String booleanArgString(Arg a)
258   {
259     StringBuilder sb = new StringBuilder(a.argString());
260     if (a.hasOption(Opt.BOOLEAN))
261     {
262       sb.append('/');
263       sb.append(a.negateArgString());
264     }
265     return sb.toString();
266   }
267
268   public static final String usage()
269   {
270     StringBuilder sb = new StringBuilder();
271
272     sb.append("Usage: jalview [args]");
273     sb.append(System.lineSeparator());
274
275     int maxArgLength = 0;
276     for (Arg a : EnumSet.allOf(Arg.class))
277     {
278       if (a.hasOption(Opt.PRIVATE))
279         continue;
280       StringBuilder argSb = new StringBuilder();
281       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
282               : a.argString());
283       if (a.hasOption(Opt.STRING))
284         argSb.append("=value");
285       if (argSb.length() > maxArgLength)
286         maxArgLength = argSb.length();
287     }
288
289     // might want to sort these
290     for (Arg a : EnumSet.allOf(Arg.class))
291     {
292       if (a.hasOption(Opt.PRIVATE))
293         continue;
294       StringBuilder argSb = new StringBuilder();
295       argSb.append(a.hasOption(Opt.BOOLEAN) ? booleanArgString(a)
296               : a.argString());
297       if (a.hasOption(Opt.STRING))
298         argSb.append("=value");
299       sb.append(String.format("%-" + maxArgLength + "s  - %s",
300               argSb.toString(), a.getDescription()));
301
302       List<String> options = new ArrayList<>();
303
304       if (a.hasOption(Opt.BOOLEAN))
305       {
306         options.add("default " + (a.getDefaultBoolValue() ? a.argString()
307                 : a.negateArgString()));
308       }
309
310       if (a.hasOption(Opt.MULTI))
311       {
312         options.add("multiple");
313       }
314
315       if (a.hasOption(Opt.LINKED))
316       {
317         options.add("can be linked");
318       }
319
320       if (a.hasOption(Opt.GLOB))
321       {
322         options.add("allows file globs");
323       }
324
325       if (a.hasOption(Opt.ALLOWSUBSTITUTIONS))
326       {
327         options.add("allows substitutions");
328       }
329
330       if (options.size() > 0)
331       {
332         sb.append(" (");
333         sb.append(String.join("; ", options));
334         sb.append(')');
335       }
336       sb.append(System.lineSeparator());
337     }
338     return sb.toString();
339   }
340 }