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 public static BootstrapArgs getBootstrapArgs(String[] args)
30 List<String> argList = new ArrayList<>(Arrays.asList(args));
31 return new BootstrapArgs(argList);
34 public static BootstrapArgs getBootstrapArgs(List<String> args)
36 return new BootstrapArgs(args);
39 private BootstrapArgs(List<String> args)
44 private void parse(List<String> args, File inArgFile)
48 // avoid looping argFiles
49 if (inArgFile != null)
51 if (argFiles.contains(inArgFile))
54 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
57 argFiles.add(inArgFile);
60 for (int i = 0; i < args.size(); i++)
62 String arg = args.get(i);
63 // look for double-dash, e.g. --arg
64 if (arg.startsWith(ArgParser.DOUBLEDASH))
66 String argName = null;
70 argName = arg.substring(ArgParser.DOUBLEDASH.length());
72 // look for equals e.g. --arg=value
73 int equalPos = argName.indexOf(ArgParser.EQUALS);
76 val = argName.substring(equalPos + 1);
77 argName = argName.substring(0, equalPos);
80 // check for boolean prepended by "no"
81 if (argName.startsWith(ArgParser.NEGATESTRING)
82 && ArgParser.argMap.containsKey(
83 argName.substring(ArgParser.NEGATESTRING.length())))
86 argName = argName.substring(ArgParser.NEGATESTRING.length());
89 // look for type modification e.g. --help-opening
90 int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
93 String potentialArgName = argName.substring(0, dashPos);
94 Arg potentialArg = ArgParser.argMap.get(potentialArgName);
95 if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
97 String typeName = argName.substring(dashPos + 1);
100 type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
101 } catch (IllegalArgumentException e)
105 argName = argName.substring(0, dashPos);
109 if (ArgParser.argMap.containsKey(argName) && val == null)
114 Arg a = ArgParser.argMap.get(argName);
118 for (Opt opt : a.getOptions())
120 if (!argsOptions.contains(opt))
122 argsOptions.add(opt);
127 if (a == null || !a.hasOption(Opt.BOOTSTRAP))
129 // not a valid bootstrap arg
133 if (a.hasOption(Opt.STRING))
135 List<String> vals = null;
138 vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
142 if (a.hasOption(Opt.GLOB))
144 vals = FileUtils.getFilenamesFromGlob(val);
148 vals = new ArrayList<>();
152 addAll(a, type, vals);
154 if (a == Arg.ARGFILE)
156 for (String filename : vals)
158 File argFile = new File(filename);
159 parse(ArgParser.readArgFile(argFile), argFile);
171 public boolean contains(Arg a)
173 return bootstrapArgMap.containsKey(a);
176 public boolean containsType(Type t)
178 for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
180 for (Map.Entry<Type, String> e : l)
189 public List<Arg> getArgsOfType(Type t)
191 return getArgsOfType(t, new Opt[] {});
194 public List<Arg> getArgsOfType(Type t, Opt... opts)
196 List<Arg> args = new ArrayList<>();
197 for (Arg a : bootstrapArgMap.keySet())
199 if (!a.hasAllOptions(opts))
202 List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
203 if (l.stream().anyMatch(e -> e.getKey() == t))
211 public List<Map.Entry<Type, String>> getList(Arg a)
213 return bootstrapArgMap.get(a);
216 public List<String> getValueList(Arg a)
218 return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
219 .collect(Collectors.toList());
222 private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
224 List<Map.Entry<Type, String>> l = getList(a);
227 l = new ArrayList<>();
233 private void putList(Arg a, List<Map.Entry<Type, String>> l)
235 bootstrapArgMap.put(a, l);
239 * Creates a new list if not used before,
240 * adds the value unless the existing list is non-empty
241 * and the arg is not MULTI (so first expressed value is
244 private void add(Arg a, Type t, String s)
246 List<Map.Entry<Type, String>> l = getOrCreateList(a);
247 if (a.hasOption(Opt.MULTI) || l.size() == 0)
253 private void addAll(Arg a, Type t, List<String> al)
255 List<Map.Entry<Type, String>> l = getOrCreateList(a);
256 if (a.hasOption(Opt.MULTI))
263 else if (l.size() == 0 && al.size() > 0)
265 l.add(entry(t, al.get(0)));
269 private static Map.Entry<Type, String> entry(Type t, String s)
271 return new AbstractMap.SimpleEntry<Type, String>(t, s);
275 * Retrieves the first value even if MULTI.
276 * A convenience for non-MULTI args.
278 public String get(Arg a)
280 if (!bootstrapArgMap.containsKey(a))
282 List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
283 return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
286 public boolean getBoolean(Arg a, boolean d)
288 if (!bootstrapArgMap.containsKey(a))
290 return Boolean.parseBoolean(get(a));
293 public boolean getBoolean(Arg a)
295 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
299 if (bootstrapArgMap.containsKey(a))
301 return Boolean.parseBoolean(get(a));
305 return a.getDefaultBoolValue();
309 public boolean argsHaveOption(Opt opt)
311 return argsOptions.contains(opt);