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.
21 package jalview.bin.argparser;
24 import java.util.AbstractMap;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.EnumSet;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Locale;
34 import java.util.stream.Collectors;
36 import jalview.bin.argparser.Arg.Opt;
37 import jalview.bin.argparser.Arg.Type;
38 import jalview.util.FileUtils;
40 public class BootstrapArgs
43 private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
45 private Set<File> argFiles = new HashSet<>();
47 private Set<Opt> argsOptions = new HashSet<>();
49 private Set<Type> argsTypes = new HashSet<>();
51 private boolean outputToStdout = false;
53 public static BootstrapArgs getBootstrapArgs(String[] args)
55 List<String> argList = new ArrayList<>(Arrays.asList(args));
56 return new BootstrapArgs(argList);
59 public static BootstrapArgs getBootstrapArgs(List<String> args)
61 return new BootstrapArgs(args);
64 private BootstrapArgs(List<String> args)
69 private void parse(List<String> args, File inArgFile)
73 // avoid looping argFiles
74 if (inArgFile != null)
76 if (argFiles.contains(inArgFile))
78 jalview.bin.Console.errPrintln(
79 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
82 argFiles.add(inArgFile);
85 for (int i = 0; i < args.size(); i++)
87 String arg = args.get(i);
88 // look for double-dash, e.g. --arg
89 if (arg.startsWith(ArgParser.DOUBLEDASH))
91 String argName = null;
95 argName = arg.substring(ArgParser.DOUBLEDASH.length());
97 // look for equals e.g. --arg=value
98 int equalPos = argName.indexOf(ArgParser.EQUALS);
101 val = argName.substring(equalPos + 1);
102 argName = argName.substring(0, equalPos);
105 // check for boolean prepended by "no"
106 if (argName.startsWith(ArgParser.NEGATESTRING)
107 && ArgParser.argMap.containsKey(
108 argName.substring(ArgParser.NEGATESTRING.length())))
111 argName = argName.substring(ArgParser.NEGATESTRING.length());
114 // look for type modification e.g. --help-opening
115 int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
118 String potentialArgName = argName.substring(0, dashPos);
119 Arg potentialArg = ArgParser.argMap.get(potentialArgName);
120 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
122 String typeName = argName.substring(dashPos + 1);
125 type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
126 } catch (IllegalArgumentException e)
130 argName = argName.substring(0, dashPos);
134 // after all other args, look for Opt.PREFIX args if still not found
135 if (!ArgParser.argMap.containsKey(argName))
137 for (Arg potentialArg : EnumSet.allOf(Arg.class))
139 if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null
140 && argName.startsWith(potentialArg.getName())
143 val = argName.substring(potentialArg.getName().length())
144 + ArgParser.EQUALS + val;
145 argName = argName.substring(0,
146 potentialArg.getName().length());
152 Arg a = ArgParser.argMap.get(argName);
156 for (Opt opt : a.getOptions())
158 if (!argsOptions.contains(opt))
160 argsOptions.add(opt);
163 for (Type t : a.getTypes())
165 if (!argsTypes.contains(t))
172 if (a == null || !a.hasOption(Opt.BOOTSTRAP))
174 // not a bootstrap arg
176 // make a check for an output going to stdout
177 if (a != null && a.hasOption(Opt.OUTPUTFILE)
178 && a.hasOption(Opt.STDOUT))
180 if (val == null && i + 1 < args.size())
182 val = args.get(i + 1);
184 if (val.startsWith("[") && val.indexOf(']') > 0)
186 val = val.substring(val.indexOf(']') + 1);
189 if (ArgParser.STDOUTFILENAME.equals(val))
191 this.outputToStdout = true;
198 if (a.hasOption(Opt.STRING))
200 List<String> vals = null;
203 vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
207 if (a.hasOption(Opt.GLOB))
209 vals = FileUtils.getFilenamesFromGlob(val);
213 vals = new ArrayList<>();
217 addAll(a, type, vals);
219 if (a == Arg.ARGFILE)
221 for (String filename : vals)
223 File argFile = new File(filename);
224 parse(ArgParser.readArgFile(argFile), argFile);
240 // if in an argfile, remove it from the hashset so it can be re-used in
242 if (inArgFile != null)
244 argFiles.remove(inArgFile);
248 public boolean contains(Arg a)
250 return bootstrapArgMap.containsKey(a);
253 public boolean containsType(Type t)
255 for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
257 for (Map.Entry<Type, String> e : l)
266 public List<Arg> getArgsOfType(Type t)
268 return getArgsOfType(t, new Opt[] {});
271 public List<Arg> getArgsOfType(Type t, Opt... opts)
273 List<Arg> args = new ArrayList<>();
274 for (Arg a : bootstrapArgMap.keySet())
276 if (!a.hasAllOptions(opts))
279 List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
280 if (l.stream().anyMatch(e -> e.getKey() == t))
288 public List<Map.Entry<Type, String>> getList(Arg a)
290 return bootstrapArgMap.get(a);
293 public List<String> getValueList(Arg a)
295 return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
296 .collect(Collectors.toList());
299 private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
301 List<Map.Entry<Type, String>> l = getList(a);
304 l = new ArrayList<>();
310 private void putList(Arg a, List<Map.Entry<Type, String>> l)
312 bootstrapArgMap.put(a, l);
316 * Creates a new list if not used before,
317 * adds the value unless the existing list is non-empty
318 * and the arg is not MULTI (so first expressed value is
321 private void add(Arg a, Type t, String s)
323 List<Map.Entry<Type, String>> l = getOrCreateList(a);
324 if (a.hasOption(Opt.MULTIVALUE) || l.size() == 0)
330 private void addAll(Arg a, Type t, List<String> al)
332 List<Map.Entry<Type, String>> l = getOrCreateList(a);
333 if (a.hasOption(Opt.MULTIVALUE))
340 else if (l.size() == 0 && al.size() > 0)
342 l.add(entry(t, al.get(0)));
346 private static Map.Entry<Type, String> entry(Type t, String s)
348 return new AbstractMap.SimpleEntry<Type, String>(t, s);
352 * Retrieves the first value even if MULTI.
353 * A convenience for non-MULTI args.
355 public String getValue(Arg a)
357 if (!bootstrapArgMap.containsKey(a))
359 List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
360 return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
363 public boolean getBoolean(Arg a, boolean d)
365 if (!bootstrapArgMap.containsKey(a))
367 return Boolean.parseBoolean(getValue(a));
370 public boolean getBoolean(Arg a)
372 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
376 if (bootstrapArgMap.containsKey(a))
378 return Boolean.parseBoolean(getValue(a));
382 return a.getDefaultBoolValue();
386 public boolean argsHaveOption(Opt opt)
388 return argsOptions.contains(opt);
391 public boolean argsHaveType(Type type)
393 return argsTypes.contains(type);
396 public boolean isHeadless()
398 boolean isHeadless = false;
399 if (this.argsHaveType(Type.HELP))
401 // --help, --help-all, ... specified => headless
404 else if (this.contains(Arg.VERSION))
406 // --version specified => headless
409 else if (this.contains(Arg.GUI))
411 // --gui specified => forced NOT headless
412 isHeadless = !this.getBoolean(Arg.GUI);
414 else if (this.contains(Arg.HEADLESS))
416 // --headless has been specified on the command line => headless
417 isHeadless = this.getBoolean(Arg.HEADLESS);
419 else if (this.argsHaveOption(Opt.OUTPUTFILE))
421 // --output file.fa, --image pic.png, --structureimage struct.png =>
422 // assume headless unless above has been already specified
428 public boolean outputToStdout()
430 return this.outputToStdout;