2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.net.URLDecoder;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.Enumeration;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Locale;
34 import jalview.util.Platform;
36 public class ArgParser
38 private static final String NEGATESTRING = "no";
40 private static final String DEFAULTLINKEDID = "";
42 private static enum Opt
44 BOOLEAN, STRING, UNARY, MULTI, LINKED, ORDERED
50 NOCALCULATION, NOMENUBAR, NOSTATUS, SHOWOVERVIEW, ANNOTATIONS, COLOUR,
51 FEATURES, GROOVY, GROUPS, HEADLESS, JABAWS, NOANNOTATION, NOANNOTATION2,
52 NODISPLAY, NOGUI, NONEWS, NOQUESTIONNAIRE, NOSORTBYTREE, NOUSAGESTATS,
53 OPEN, OPEN2, PROPS, QUESTIONNAIRE, SETPROP, SORTBYTREE, TREE, VDOC,
56 HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, ANNOTATIONS,
57 COLOUR, FEATURES, GROOVY, GROUPS, HEADLESS, JABAWS, ANNOTATION,
58 ANNOTATION2, DISPLAY, GUI, NEWS, NOQUESTIONNAIRE, SORTBYTREE,
59 USAGESTATS, OPEN, OPEN2, PROPS, QUESTIONNAIRE, SETPROP, TREE, VDOC,
60 VSESS, OUTPUT, OUTPUTTYPE, SSANNOTATION, NOTEMPFAC, TEMPFAC,
61 TEMPFAC_LABEL, TEMPFAC_DESC, TEMPFAC_SHADING, TITLE;
65 HELP.setOptions(Opt.UNARY);
66 CALCULATION.setOptions(true, Opt.BOOLEAN); // default "true" implies only
67 // expecting "--nocalculation"
68 MENUBAR.setOptions(true, Opt.BOOLEAN);
69 STATUS.setOptions(true, Opt.BOOLEAN);
70 SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED);
71 ANNOTATIONS.setOptions(Opt.STRING, Opt.LINKED);
72 COLOUR.setOptions(Opt.STRING, Opt.LINKED);
73 FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
74 GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
75 GROUPS.setOptions(Opt.STRING, Opt.LINKED);
76 HEADLESS.setOptions(Opt.UNARY);
77 JABAWS.setOptions(Opt.STRING);
78 ANNOTATION.setOptions(true, Opt.BOOLEAN);
79 ANNOTATION2.setOptions(true, Opt.BOOLEAN);
80 DISPLAY.setOptions(true, Opt.BOOLEAN);
81 GUI.setOptions(true, Opt.BOOLEAN);
82 NEWS.setOptions(true, Opt.BOOLEAN);
83 NOQUESTIONNAIRE.setOptions(Opt.UNARY); // unary as --questionnaire=val
84 // expects a string value
85 SORTBYTREE.setOptions(true, Opt.BOOLEAN);
86 USAGESTATS.setOptions(true, Opt.BOOLEAN);
87 OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
88 OPEN2.setOptions(Opt.STRING, Opt.LINKED);
89 PROPS.setOptions(Opt.STRING);
90 QUESTIONNAIRE.setOptions(Opt.STRING);
91 SETPROP.setOptions(Opt.STRING);
92 TREE.setOptions(Opt.STRING);
94 VDOC.setOptions(Opt.UNARY);
95 VSESS.setOptions(Opt.UNARY);
97 OUTPUT.setOptions(Opt.STRING, Opt.LINKED);
98 OUTPUTTYPE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI);
100 SSANNOTATION.setOptions(Opt.BOOLEAN, Opt.LINKED);
101 NOTEMPFAC.setOptions(Opt.UNARY, Opt.LINKED);
102 TEMPFAC.setOptions(Opt.STRING, Opt.LINKED);
103 TEMPFAC_LABEL.setOptions(Opt.STRING, Opt.LINKED);
104 TEMPFAC_DESC.setOptions(Opt.STRING, Opt.LINKED);
105 TEMPFAC_SHADING.setOptions(Opt.STRING, Opt.LINKED);
106 TITLE.setOptions(Opt.STRING, Opt.LINKED);
109 private final String[] argNames;
111 private Opt[] argOptions;
113 private boolean defaultBoolValue = false;
115 public String toLongString()
117 StringBuilder sb = new StringBuilder();
118 sb.append("Arg: ").append(this.name());
119 for (String name : getNames())
121 sb.append(", '").append(name).append("'");
123 sb.append("\nOptions: ");
124 boolean first = true;
125 for (Opt o : argOptions)
131 sb.append(o.toString());
135 return sb.toString();
143 private Arg(String... names)
145 int length = (names == null || names.length == 0
146 || (names.length == 1 && names[0] == null)) ? 1
148 this.argNames = new String[length];
149 this.argNames[0] = this.getName();
151 System.arraycopy(names, 0, this.argNames, 1, names.length);
154 public String[] getNames()
159 public String getName()
161 return this.name().toLowerCase(Locale.ROOT).replace('_', '-');
165 public final String toString()
170 public boolean hasOption(Opt o)
172 if (argOptions == null)
174 for (Opt option : argOptions)
182 protected void setOptions(Opt... options)
184 setOptions(false, options);
187 protected void setOptions(boolean defaultBoolValue, Opt... options)
189 this.defaultBoolValue = defaultBoolValue;
190 argOptions = options;
193 protected boolean getDefaultBoolValue()
195 return defaultBoolValue;
199 public static class ArgValues
203 private int argCount = 0;
205 private boolean boolValue = false;
207 private boolean negated = false;
209 private List<String> argsList;
211 protected ArgValues(Arg a)
214 this.argsList = new ArrayList<String>();
215 this.boolValue = arg.getDefaultBoolValue();
223 protected int getCount()
228 protected void incrementCount()
233 protected void setNegated(boolean b)
238 protected boolean isNegated()
243 protected void setBoolean(boolean b)
248 protected boolean getBoolean()
250 return this.boolValue;
254 public String toString()
256 if (argsList == null)
258 StringBuilder sb = new StringBuilder();
259 sb.append(arg.toLongString());
260 if (arg.hasOption(Opt.BOOLEAN) || arg.hasOption(Opt.UNARY))
261 sb.append("Boolean: ").append(boolValue).append("; Default: ")
262 .append(arg.getDefaultBoolValue()).append("; Negated: ")
263 .append(negated).append("\n");
264 if (arg.hasOption(Opt.STRING))
266 sb.append("Values:");
267 boolean first = true;
268 for (String v : argsList)
273 sb.append(v).append("'");
278 sb.append("Count: ").append(argCount).append("\n");
279 return sb.toString();
282 protected void addValue()
287 protected void addValue(String val)
289 addValue(val, false);
292 protected void addValue(String val, boolean noDuplicates)
294 if ((!arg.hasOption(Opt.MULTI) && argsList.size() > 0)
295 || (noDuplicates && argsList.contains(val)))
297 if (argsList == null)
299 Console.warn("** inst");
300 argsList = new ArrayList<String>();
305 protected boolean hasValue(String val)
307 return argsList.contains(val);
310 protected String getValue()
312 if (arg.hasOption(Opt.MULTI))
313 Console.warn("Requesting single value for multi value argument");
314 return argsList.size() > 0 ? argsList.get(0) : null;
317 protected List<String> getValues()
324 private List<String> vargs = null;
326 private boolean isApplet;
328 // private AppletParams appletParams;
330 public boolean isApplet()
335 public String nextValue()
337 return vargs.remove(0);
345 public String getValue(String arg)
347 return getValue(arg, false);
350 public String getValue(String arg, boolean utf8decode)
352 int index = vargs.indexOf(arg);
353 String dc = null, ret = null;
356 ret = vargs.get(index + 1).toString();
359 if (utf8decode && ret != null)
363 dc = URLDecoder.decode(ret, "UTF-8");
365 } catch (Exception e)
367 // TODO: log failure to decode
375 public Object getAppletValue(String key, String def, boolean asString)
378 return (appletParams == null ? null
379 : (value = appletParams.get(key.toLowerCase())) == null ? def
380 : asString ? "" + value : value);
385 private static final Map<String, Arg> argMap;
387 private Map<String, HashMap<Arg, ArgValues>> linkedArgs = new HashMap<>();
389 private List<String> linkedOrder = null;
391 private List<Arg> argList;
395 argMap = new HashMap<>();
396 for (Arg a : EnumSet.allOf(Arg.class))
398 ARGNAME: for (String argName : a.getNames())
400 if (argMap.containsKey(argName))
402 Console.warn("Trying to add argument name multiple times: '"
403 + argName + "'"); // RESTORE THIS WHEN MERGED
404 if (argMap.get(argName) != a)
407 "Trying to add argument name multiple times for different Args: '"
408 + argMap.get(argName).getName() + ":" + argName
409 + "' and '" + a.getName() + ":" + argName
414 argMap.put(argName, a);
419 public ArgParser(String[] args)
422 vargs = new ArrayList<>();
423 isApplet = (args.length > 0 && args[0].startsWith("<applet"));
426 // appletParams = AppletParams.getAppletParams(args, vargs);
435 // AppletParams.getAppletParams(Platform.getAppletInfoAsMap(), vargs);
437 for (int i = 0; i < args.length; i++)
439 String arg = args[i].trim();
440 if (arg.charAt(0) == '-')
442 arg = arg.substring(1);
449 Enumeration<String> argE = Collections.enumeration(Arrays.asList(args));
450 ARG: while (argE.hasMoreElements())
452 String arg = argE.nextElement();
453 String argName = null;
455 String linkedId = null;
456 if (arg.startsWith("--"))
458 int equalPos = arg.indexOf('=');
461 argName = arg.substring(2, equalPos);
462 val = arg.substring(equalPos + 1);
466 argName = arg.substring(2);
468 int idOpen = argName.indexOf('[');
469 int idClose = argName.indexOf(']');
471 if (idOpen > -1 && idClose == argName.length() - 1)
473 linkedId = argName.substring(idOpen + 1, idClose);
474 argName = argName.substring(0, idOpen);
477 Arg a = argMap.get(argName);
478 // check for boolean prepended by "no"
479 boolean negated = false;
480 if (a == null && argName.startsWith(NEGATESTRING) && argMap
481 .containsKey(argName.substring(NEGATESTRING.length())))
483 argName = argName.substring(NEGATESTRING.length());
484 a = argMap.get(argName);
488 // check for config errors
492 Console.error("Argument '" + arg + "' not recognised. Ignoring.");
495 if (!a.hasOption(Opt.BOOLEAN) && negated)
497 // used "no" with a non-boolean option
498 Console.error("Argument '--" + NEGATESTRING + argName
499 + "' not a boolean option. Ignoring.");
502 if (!a.hasOption(Opt.STRING) && equalPos > -1)
504 // set --argname=value when arg does not accept values
505 Console.error("Argument '--" + argName
506 + "' does not expect a value (given as '" + arg
510 if (!a.hasOption(Opt.LINKED) && linkedId != null)
512 // set --argname[linkedId] when arg does not use linkedIds
513 Console.error("Argument '--" + argName
514 + "' does not expect a linked id (given as '" + arg
519 if (a.hasOption(Opt.STRING) && equalPos == -1)
521 // take next arg as value if required, and '=' was not found
522 if (!argE.hasMoreElements())
524 // no value to take for arg, which wants a value
525 Console.error("Argument '" + a.getName()
526 + "' requires a value, none given. Ignoring.");
529 val = argE.nextElement();
532 // use default linkedId for linked arguments
533 if (a.hasOption(Opt.LINKED) && linkedId == null)
534 linkedId = DEFAULTLINKEDID;
536 if (!linkedArgs.containsKey(linkedId))
537 linkedArgs.put(linkedId, new HashMap<>());
539 Map<Arg, ArgValues> valuesMap = linkedArgs.get(linkedId);
540 if (!valuesMap.containsKey(a))
541 valuesMap.put(a, new ArgValues(a));
543 ArgValues values = valuesMap.get(a);
546 values = new ArgValues(a);
548 // store appropriate value
549 if (a.hasOption(Opt.STRING))
551 values.addValue(val);
553 else if (a.hasOption(Opt.BOOLEAN))
555 values.setBoolean(!negated);
556 values.setNegated(negated);
558 else if (a.hasOption(Opt.UNARY))
560 values.setBoolean(true);
562 values.incrementCount();
564 // store in appropriate place
565 if (a.hasOption(Opt.LINKED))
567 // allow a default linked id for single usage
568 if (linkedId == null)
569 linkedId = DEFAULTLINKEDID;
570 // store the order of linkedIds
571 if (linkedOrder == null)
572 linkedOrder = new ArrayList<>();
573 if (!linkedOrder.contains(linkedId))
574 linkedOrder.add(linkedId);
576 // store the ArgValues
577 valuesMap.put(a, values);
579 // store arg in the list of args
581 argList = new ArrayList<>();
582 if (!argList.contains(a))
588 public boolean isSet(Arg a)
590 return a.hasOption(Opt.LINKED) ? isSet("", a) : isSet(null, a);
593 public boolean isSet(String linkedId, Arg a)
595 Map<Arg, ArgValues> m = linkedArgs.get(linkedId);
596 return m == null ? false : m.containsKey(a);
599 public boolean getBool(Arg a)
601 if (!a.hasOption(Opt.BOOLEAN))
603 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
606 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
609 public boolean getBool(String linkedId, Arg a)
611 Map<Arg, ArgValues> m = linkedArgs.get(linkedId);
613 return a.getDefaultBoolValue();
614 ArgValues v = m.get(a);
615 return v == null ? a.getDefaultBoolValue() : v.getBoolean();
618 public List<String> linkedIds()
623 public HashMap<Arg, ArgValues> linkedArgs(String id)
625 return linkedArgs.get(id);
629 public String toString()
631 StringBuilder sb = new StringBuilder();
632 sb.append("UNLINKED\n");
633 sb.append(argMapToString(linkedArgs.get(null)));
634 if (linkedIds() != null)
636 sb.append("LINKED\n");
637 for (String id : linkedIds())
639 // already listed these as UNLINKED args
643 Map<Arg, ArgValues> m = linkedArgs(id);
644 sb.append("ID: '").append(id).append("'\n");
645 sb.append(argMapToString(m));
648 return sb.toString();
651 private static String argMapToString(Map<Arg, ArgValues> m)
655 StringBuilder sb = new StringBuilder();
656 for (Arg a : m.keySet())
658 ArgValues v = m.get(a);
659 sb.append(v.toString());
662 return sb.toString();