1 package jalview.bin.argparser;
4 import java.util.AbstractMap;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.HashMap;
8 import java.util.HashSet;
10 import java.util.Locale;
13 import java.util.stream.Collectors;
15 import jalview.bin.argparser.Arg.Opt;
16 import jalview.bin.argparser.Arg.Type;
17 import jalview.util.FileUtils;
19 public class BootstrapArgs
22 private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
24 private Set<File> argFiles = new HashSet<>();
26 private Set<Opt> argsOptions = new HashSet<>();
28 private Set<Type> argsTypes = new HashSet<>();
30 public static BootstrapArgs getBootstrapArgs(String[] args)
32 List<String> argList = new ArrayList<>(Arrays.asList(args));
33 return new BootstrapArgs(argList);
36 public static BootstrapArgs getBootstrapArgs(List<String> args)
38 return new BootstrapArgs(args);
41 private BootstrapArgs(List<String> args)
46 private void parse(List<String> args, File inArgFile)
50 // avoid looping argFiles
51 if (inArgFile != null)
53 if (argFiles.contains(inArgFile))
56 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
59 argFiles.add(inArgFile);
62 for (int i = 0; i < args.size(); i++)
64 String arg = args.get(i);
65 // look for double-dash, e.g. --arg
66 if (arg.startsWith(ArgParser.DOUBLEDASH))
68 String argName = null;
72 argName = arg.substring(ArgParser.DOUBLEDASH.length());
74 // look for equals e.g. --arg=value
75 int equalPos = argName.indexOf(ArgParser.EQUALS);
78 val = argName.substring(equalPos + 1);
79 argName = argName.substring(0, equalPos);
82 // check for boolean prepended by "no"
83 if (argName.startsWith(ArgParser.NEGATESTRING)
84 && ArgParser.argMap.containsKey(
85 argName.substring(ArgParser.NEGATESTRING.length())))
88 argName = argName.substring(ArgParser.NEGATESTRING.length());
91 // look for type modification e.g. --help-opening
92 int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
95 String potentialArgName = argName.substring(0, dashPos);
96 Arg potentialArg = ArgParser.argMap.get(potentialArgName);
97 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
99 String typeName = argName.substring(dashPos + 1);
102 type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
103 } catch (IllegalArgumentException e)
107 argName = argName.substring(0, dashPos);
111 if (ArgParser.argMap.containsKey(argName) && val == null)
116 Arg a = ArgParser.argMap.get(argName);
120 for (Opt opt : a.getOptions())
122 if (!argsOptions.contains(opt))
124 argsOptions.add(opt);
127 Type t = a.getType();
128 if (!argsTypes.contains(t))
134 if (a == null || !a.hasOption(Opt.BOOTSTRAP))
136 // not a valid bootstrap arg
140 if (a.hasOption(Opt.STRING))
142 List<String> vals = null;
145 vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
149 if (a.hasOption(Opt.GLOB))
151 vals = FileUtils.getFilenamesFromGlob(val);
155 vals = new ArrayList<>();
159 addAll(a, type, vals);
161 if (a == Arg.ARGFILE)
163 for (String filename : vals)
165 File argFile = new File(filename);
166 parse(ArgParser.readArgFile(argFile), argFile);
178 public boolean contains(Arg a)
180 return bootstrapArgMap.containsKey(a);
183 public boolean containsType(Type t)
185 for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
187 for (Map.Entry<Type, String> e : l)
196 public List<Arg> getArgsOfType(Type t)
198 return getArgsOfType(t, new Opt[] {});
201 public List<Arg> getArgsOfType(Type t, Opt... opts)
203 List<Arg> args = new ArrayList<>();
204 for (Arg a : bootstrapArgMap.keySet())
206 if (!a.hasAllOptions(opts))
209 List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
210 if (l.stream().anyMatch(e -> e.getKey() == t))
218 public List<Map.Entry<Type, String>> getList(Arg a)
220 return bootstrapArgMap.get(a);
223 public List<String> getValueList(Arg a)
225 return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
226 .collect(Collectors.toList());
229 private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
231 List<Map.Entry<Type, String>> l = getList(a);
234 l = new ArrayList<>();
240 private void putList(Arg a, List<Map.Entry<Type, String>> l)
242 bootstrapArgMap.put(a, l);
246 * Creates a new list if not used before,
247 * adds the value unless the existing list is non-empty
248 * and the arg is not MULTI (so first expressed value is
251 private void add(Arg a, Type t, String s)
253 List<Map.Entry<Type, String>> l = getOrCreateList(a);
254 if (a.hasOption(Opt.MULTI) || l.size() == 0)
260 private void addAll(Arg a, Type t, List<String> al)
262 List<Map.Entry<Type, String>> l = getOrCreateList(a);
263 if (a.hasOption(Opt.MULTI))
270 else if (l.size() == 0 && al.size() > 0)
272 l.add(entry(t, al.get(0)));
276 private static Map.Entry<Type, String> entry(Type t, String s)
278 return new AbstractMap.SimpleEntry<Type, String>(t, s);
282 * Retrieves the first value even if MULTI.
283 * A convenience for non-MULTI args.
285 public String getValue(Arg a)
287 if (!bootstrapArgMap.containsKey(a))
289 List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
290 return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
293 public boolean getBoolean(Arg a, boolean d)
295 if (!bootstrapArgMap.containsKey(a))
297 return Boolean.parseBoolean(getValue(a));
300 public boolean getBoolean(Arg a)
302 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
306 if (bootstrapArgMap.containsKey(a))
308 return Boolean.parseBoolean(getValue(a));
312 return a.getDefaultBoolValue();
316 public boolean argsHaveOption(Opt opt)
318 return argsOptions.contains(opt);
321 public boolean argsHaveType(Type type)
323 return argsTypes.contains(type);
326 public boolean isHeadless()
328 boolean isHeadless = false;
329 if (this.argsHaveType(Type.HELP))
331 // --help, --help-all, ... specified => headless
334 else if (this.contains(Arg.VERSION))
336 // --version specified => headless
339 else if (this.contains(Arg.GUI))
341 // --gui specified => forced NOT headless
342 isHeadless = !this.getBoolean(Arg.GUI);
344 else if (this.contains(Arg.HEADLESS))
346 // --headless, --noheadless specified => use value
347 isHeadless = this.getBoolean(Arg.HEADLESS);
349 else if (this.argsHaveOption(Opt.OUTPUTFILE))
351 // --output file.fa, --image pic.png, --structureimage struct.png =>
352 // assume headless unless above has been already specified