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 Type t = a.getType();
164 if (!argsTypes.contains(t))
170 if (a == null || !a.hasOption(Opt.BOOTSTRAP))
172 // not a bootstrap arg
174 // make a check for an output going to stdout
175 if (a != null && a.hasOption(Opt.OUTPUTFILE)
176 && a.hasOption(Opt.STDOUT))
178 if (val == null && i + 1 < args.size())
180 val = args.get(i + 1);
182 if (val.startsWith("[") && val.indexOf(']') > 0)
184 val = val.substring(val.indexOf(']') + 1);
187 if (ArgParser.STDOUTFILENAME.equals(val))
189 this.outputToStdout = true;
196 if (a.hasOption(Opt.STRING))
198 List<String> vals = null;
201 vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
205 if (a.hasOption(Opt.GLOB))
207 vals = FileUtils.getFilenamesFromGlob(val);
211 vals = new ArrayList<>();
215 addAll(a, type, vals);
217 if (a == Arg.ARGFILE)
219 for (String filename : vals)
221 File argFile = new File(filename);
222 parse(ArgParser.readArgFile(argFile), argFile);
238 // if in an argfile, remove it from the hashset so it can be re-used in
240 if (inArgFile != null)
242 argFiles.remove(inArgFile);
246 public boolean contains(Arg a)
248 return bootstrapArgMap.containsKey(a);
251 public boolean containsType(Type t)
253 for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
255 for (Map.Entry<Type, String> e : l)
264 public List<Arg> getArgsOfType(Type t)
266 return getArgsOfType(t, new Opt[] {});
269 public List<Arg> getArgsOfType(Type t, Opt... opts)
271 List<Arg> args = new ArrayList<>();
272 for (Arg a : bootstrapArgMap.keySet())
274 if (!a.hasAllOptions(opts))
277 List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
278 if (l.stream().anyMatch(e -> e.getKey() == t))
286 public List<Map.Entry<Type, String>> getList(Arg a)
288 return bootstrapArgMap.get(a);
291 public List<String> getValueList(Arg a)
293 return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
294 .collect(Collectors.toList());
297 private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
299 List<Map.Entry<Type, String>> l = getList(a);
302 l = new ArrayList<>();
308 private void putList(Arg a, List<Map.Entry<Type, String>> l)
310 bootstrapArgMap.put(a, l);
314 * Creates a new list if not used before,
315 * adds the value unless the existing list is non-empty
316 * and the arg is not MULTI (so first expressed value is
319 private void add(Arg a, Type t, String s)
321 List<Map.Entry<Type, String>> l = getOrCreateList(a);
322 if (a.hasOption(Opt.MULTIVALUE) || l.size() == 0)
328 private void addAll(Arg a, Type t, List<String> al)
330 List<Map.Entry<Type, String>> l = getOrCreateList(a);
331 if (a.hasOption(Opt.MULTIVALUE))
338 else if (l.size() == 0 && al.size() > 0)
340 l.add(entry(t, al.get(0)));
344 private static Map.Entry<Type, String> entry(Type t, String s)
346 return new AbstractMap.SimpleEntry<Type, String>(t, s);
350 * Retrieves the first value even if MULTI.
351 * A convenience for non-MULTI args.
353 public String getValue(Arg a)
355 if (!bootstrapArgMap.containsKey(a))
357 List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
358 return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
361 public boolean getBoolean(Arg a, boolean d)
363 if (!bootstrapArgMap.containsKey(a))
365 return Boolean.parseBoolean(getValue(a));
368 public boolean getBoolean(Arg a)
370 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
374 if (bootstrapArgMap.containsKey(a))
376 return Boolean.parseBoolean(getValue(a));
380 return a.getDefaultBoolValue();
384 public boolean argsHaveOption(Opt opt)
386 return argsOptions.contains(opt);
389 public boolean argsHaveType(Type type)
391 return argsTypes.contains(type);
394 public boolean isHeadless()
396 boolean isHeadless = false;
397 if (this.argsHaveType(Type.HELP))
399 // --help, --help-all, ... specified => headless
402 else if (this.contains(Arg.VERSION))
404 // --version specified => headless
407 else if (this.contains(Arg.GUI))
409 // --gui specified => forced NOT headless
410 isHeadless = !this.getBoolean(Arg.GUI);
412 else if (this.contains(Arg.HEADLESS))
414 // --headless has been specified on the command line => headless
415 isHeadless = this.getBoolean(Arg.HEADLESS);
417 else if (this.argsHaveOption(Opt.OUTPUTFILE))
419 // --output file.fa, --image pic.png, --structureimage struct.png =>
420 // assume headless unless above has been already specified
426 public boolean outputToStdout()
428 return this.outputToStdout;