package jalview.bin.argparser; import java.io.File; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import jalview.bin.argparser.Arg.Opt; import jalview.bin.argparser.Arg.Type; import jalview.util.FileUtils; public class BootstrapArgs { // only need one private Map>> bootstrapArgMap = new HashMap<>(); private Set argFiles = new HashSet<>(); public static BootstrapArgs getBootstrapArgs(String[] args) { List argList = new ArrayList<>(Arrays.asList(args)); return new BootstrapArgs(argList); } public static BootstrapArgs getBootstrapArgs(List args) { return new BootstrapArgs(args); } private BootstrapArgs(List args) { parse(args, null); } private void parse(List args, File inArgFile) { if (args == null) return; // avoid looping argFiles if (inArgFile != null) { if (argFiles.contains(inArgFile)) { System.err.println( "Looped argfiles detected: '" + inArgFile.getPath() + "'"); return; } argFiles.add(inArgFile); } for (int i = 0; i < args.size(); i++) { String arg = args.get(i); // look for double-dash, e.g. --arg if (arg.startsWith(ArgParser.DOUBLEDASH)) { String argName = null; String val = null; Type type = null; // remove "--" argName = arg.substring(ArgParser.DOUBLEDASH.length()); // look for equals e.g. --arg=value int equalPos = argName.indexOf(ArgParser.EQUALS); if (equalPos > -1) { val = argName.substring(equalPos + 1); argName = argName.substring(0, equalPos); } // check for boolean prepended by "no" if (argName.startsWith(ArgParser.NEGATESTRING) && ArgParser.argMap.containsKey( argName.substring(ArgParser.NEGATESTRING.length()))) { val = "false"; argName = argName.substring(ArgParser.NEGATESTRING.length()); } // look for type modification e.g. --help-opening int dashPos = argName.indexOf(ArgParser.SINGLEDASH); if (dashPos > -1) { String potentialArgName = argName.substring(0, dashPos); Arg potentialArg = ArgParser.argMap.get(potentialArgName); if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE)) { String typeName = argName.substring(dashPos + 1); try { type = Type.valueOf(typeName.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { type = Type.INVALID; } argName = argName.substring(0, dashPos); } } if (ArgParser.argMap.containsKey(argName) && val == null) { val = "true"; } Arg a = ArgParser.argMap.get(argName); if (a == null || !a.hasOption(Opt.BOOTSTRAP)) { // not a valid bootstrap arg continue; } if (a.hasOption(Opt.STRING)) { List vals = null; if (equalPos == -1) { vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1); } else { if (a.hasOption(Opt.GLOB)) { vals = FileUtils.getFilenamesFromGlob(val); } else { vals = new ArrayList<>(); vals.add(val); } } addAll(a, type, vals); if (a == Arg.ARGFILE) { for (String filename : vals) { File argFile = new File(filename); parse(ArgParser.readArgFile(argFile), argFile); } } } else { add(a, type, val); } } } } public boolean contains(Arg a) { return bootstrapArgMap.containsKey(a); } public boolean containsType(Type t) { for (List> l : bootstrapArgMap.values()) { for (Map.Entry e : l) { if (e.getKey() == t) return true; } } return false; } public List getArgsOfType(Type t) { return getArgsOfType(t, new Opt[] {}); } public List getArgsOfType(Type t, Opt... opts) { List args = new ArrayList<>(); for (Arg a : bootstrapArgMap.keySet()) { if (!a.hasAllOptions(opts)) continue; List> l = bootstrapArgMap.get(a); if (l.stream().anyMatch(e -> e.getKey() == t)) { args.add(a); } } return args; } public List> getList(Arg a) { return bootstrapArgMap.get(a); } public List getValueList(Arg a) { return bootstrapArgMap.get(a).stream().map(e -> e.getValue()) .collect(Collectors.toList()); } private List> getOrCreateList(Arg a) { List> l = getList(a); if (l == null) { l = new ArrayList<>(); putList(a, l); } return l; } private void putList(Arg a, List> l) { bootstrapArgMap.put(a, l); } /* * Creates a new list if not used before, * adds the value unless the existing list is non-empty * and the arg is not MULTI (so first expressed value is * retained). */ private void add(Arg a, Type t, String s) { List> l = getOrCreateList(a); if (a.hasOption(Opt.MULTI) || l.size() == 0) { l.add(entry(t, s)); } } private void addAll(Arg a, Type t, List al) { List> l = getOrCreateList(a); if (a.hasOption(Opt.MULTI)) { for (String s : al) { l.add(entry(t, s)); } } else if (l.size() == 0 && al.size() > 0) { l.add(entry(t, al.get(0))); } } private static Map.Entry entry(Type t, String s) { return new AbstractMap.SimpleEntry(t, s); } /* * Retrieves the first value even if MULTI. * A convenience for non-MULTI args. */ public String get(Arg a) { if (!bootstrapArgMap.containsKey(a)) return null; List> aL = bootstrapArgMap.get(a); return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue(); } public boolean getBoolean(Arg a, boolean d) { if (!bootstrapArgMap.containsKey(a)) return d; return Boolean.parseBoolean(get(a)); } public boolean getBoolean(Arg a) { if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY))) { return false; } if (bootstrapArgMap.containsKey(a)) return Boolean.parseBoolean(get(a)); else return a.getDefaultBoolValue(); } }