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