1 package jalview.bin.argparser;
4 import java.util.AbstractMap;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.EnumSet;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Locale;
14 import java.util.stream.Collectors;
16 import jalview.bin.argparser.Arg.Opt;
17 import jalview.bin.argparser.Arg.Type;
18 import jalview.util.FileUtils;
20 public class BootstrapArgs
23 private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
25 private Set<File> argFiles = new HashSet<>();
27 private Set<Opt> argsOptions = new HashSet<>();
29 private Set<Type> argsTypes = new HashSet<>();
31 public static BootstrapArgs getBootstrapArgs(String[] args)
33 List<String> argList = new ArrayList<>(Arrays.asList(args));
34 return new BootstrapArgs(argList);
37 public static BootstrapArgs getBootstrapArgs(List<String> args)
39 return new BootstrapArgs(args);
42 private BootstrapArgs(List<String> args)
47 private void parse(List<String> args, File inArgFile)
51 // avoid looping argFiles
52 if (inArgFile != null)
54 if (argFiles.contains(inArgFile))
57 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
60 argFiles.add(inArgFile);
63 for (int i = 0; i < args.size(); i++)
65 String arg = args.get(i);
66 // look for double-dash, e.g. --arg
67 if (arg.startsWith(ArgParser.DOUBLEDASH))
69 String argName = null;
73 argName = arg.substring(ArgParser.DOUBLEDASH.length());
75 // look for equals e.g. --arg=value
76 int equalPos = argName.indexOf(ArgParser.EQUALS);
79 val = argName.substring(equalPos + 1);
80 argName = argName.substring(0, equalPos);
83 // check for boolean prepended by "no"
84 if (argName.startsWith(ArgParser.NEGATESTRING)
85 && ArgParser.argMap.containsKey(
86 argName.substring(ArgParser.NEGATESTRING.length())))
89 argName = argName.substring(ArgParser.NEGATESTRING.length());
92 // look for type modification e.g. --help-opening
93 int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
96 String potentialArgName = argName.substring(0, dashPos);
97 Arg potentialArg = ArgParser.argMap.get(potentialArgName);
98 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
100 String typeName = argName.substring(dashPos + 1);
103 type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
104 } catch (IllegalArgumentException e)
108 argName = argName.substring(0, dashPos);
112 // after all other args, look for Opt.PREFIX args if still not found
113 if (!ArgParser.argMap.containsKey(argName))
115 for (Arg potentialArg : EnumSet.allOf(Arg.class))
117 if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null
118 && argName.startsWith(potentialArg.getName())
121 val = argName.substring(potentialArg.getName().length())
122 + ArgParser.EQUALS + val;
123 argName = argName.substring(0,
124 potentialArg.getName().length());
130 if (ArgParser.argMap.containsKey(argName) && val == null)
135 Arg a = ArgParser.argMap.get(argName);
139 for (Opt opt : a.getOptions())
141 if (!argsOptions.contains(opt))
143 argsOptions.add(opt);
146 Type t = a.getType();
147 if (!argsTypes.contains(t))
153 if (a == null || !a.hasOption(Opt.BOOTSTRAP))
155 // not a valid bootstrap arg
159 if (a.hasOption(Opt.STRING))
161 List<String> vals = null;
164 vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
168 if (a.hasOption(Opt.GLOB))
170 vals = FileUtils.getFilenamesFromGlob(val);
174 vals = new ArrayList<>();
178 addAll(a, type, vals);
180 if (a == Arg.ARGFILE)
182 for (String filename : vals)
184 File argFile = new File(filename);
185 parse(ArgParser.readArgFile(argFile), argFile);
197 public boolean contains(Arg a)
199 return bootstrapArgMap.containsKey(a);
202 public boolean containsType(Type t)
204 for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
206 for (Map.Entry<Type, String> e : l)
215 public List<Arg> getArgsOfType(Type t)
217 return getArgsOfType(t, new Opt[] {});
220 public List<Arg> getArgsOfType(Type t, Opt... opts)
222 List<Arg> args = new ArrayList<>();
223 for (Arg a : bootstrapArgMap.keySet())
225 if (!a.hasAllOptions(opts))
228 List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
229 if (l.stream().anyMatch(e -> e.getKey() == t))
237 public List<Map.Entry<Type, String>> getList(Arg a)
239 return bootstrapArgMap.get(a);
242 public List<String> getValueList(Arg a)
244 return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
245 .collect(Collectors.toList());
248 private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
250 List<Map.Entry<Type, String>> l = getList(a);
253 l = new ArrayList<>();
259 private void putList(Arg a, List<Map.Entry<Type, String>> l)
261 bootstrapArgMap.put(a, l);
265 * Creates a new list if not used before,
266 * adds the value unless the existing list is non-empty
267 * and the arg is not MULTI (so first expressed value is
270 private void add(Arg a, Type t, String s)
272 List<Map.Entry<Type, String>> l = getOrCreateList(a);
273 if (a.hasOption(Opt.MULTI) || l.size() == 0)
279 private void addAll(Arg a, Type t, List<String> al)
281 List<Map.Entry<Type, String>> l = getOrCreateList(a);
282 if (a.hasOption(Opt.MULTI))
289 else if (l.size() == 0 && al.size() > 0)
291 l.add(entry(t, al.get(0)));
295 private static Map.Entry<Type, String> entry(Type t, String s)
297 return new AbstractMap.SimpleEntry<Type, String>(t, s);
301 * Retrieves the first value even if MULTI.
302 * A convenience for non-MULTI args.
304 public String getValue(Arg a)
306 if (!bootstrapArgMap.containsKey(a))
308 List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
309 return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
312 public boolean getBoolean(Arg a, boolean d)
314 if (!bootstrapArgMap.containsKey(a))
316 return Boolean.parseBoolean(getValue(a));
319 public boolean getBoolean(Arg a)
321 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
325 if (bootstrapArgMap.containsKey(a))
327 return Boolean.parseBoolean(getValue(a));
331 return a.getDefaultBoolValue();
335 public boolean argsHaveOption(Opt opt)
337 return argsOptions.contains(opt);
340 public boolean argsHaveType(Type type)
342 return argsTypes.contains(type);
345 public boolean isHeadless()
347 boolean isHeadless = false;
348 if (this.argsHaveType(Type.HELP))
350 // --help, --help-all, ... specified => headless
353 else if (this.contains(Arg.VERSION))
355 // --version specified => headless
358 else if (this.contains(Arg.GUI))
360 // --gui specified => forced NOT headless
361 isHeadless = !this.getBoolean(Arg.GUI);
363 else if (this.contains(Arg.HEADLESS))
365 // --headless, --noheadless specified => use value
366 isHeadless = this.getBoolean(Arg.HEADLESS);
368 else if (this.argsHaveOption(Opt.OUTPUTFILE))
370 // --output file.fa, --image pic.png, --structureimage struct.png =>
371 // assume headless unless above has been already specified