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