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