a6bad2449158549f4a5a9288100aaed77a433b5d
[jalview.git] / src / jalview / bin / argparser / BootstrapArgs.java
1 package jalview.bin.argparser;
2
3 import java.io.File;
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;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.stream.Collectors;
14
15 import jalview.bin.argparser.Arg.Opt;
16 import jalview.bin.argparser.Arg.Type;
17 import jalview.util.FileUtils;
18
19 public class BootstrapArgs
20 {
21   // only need one
22   private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>();
23
24   private Set<File> argFiles = new HashSet<>();
25
26   private Set<Opt> argsOptions = new HashSet<>();
27
28   public static BootstrapArgs getBootstrapArgs(String[] args)
29   {
30     List<String> argList = new ArrayList<>(Arrays.asList(args));
31     return new BootstrapArgs(argList);
32   }
33
34   public static BootstrapArgs getBootstrapArgs(List<String> args)
35   {
36     return new BootstrapArgs(args);
37   }
38
39   private BootstrapArgs(List<String> args)
40   {
41     parse(args, null);
42   }
43
44   private void parse(List<String> args, File inArgFile)
45   {
46     if (args == null)
47       return;
48     // avoid looping argFiles
49     if (inArgFile != null)
50     {
51       if (argFiles.contains(inArgFile))
52       {
53         System.err.println(
54                 "Looped argfiles detected: '" + inArgFile.getPath() + "'");
55         return;
56       }
57       argFiles.add(inArgFile);
58     }
59
60     for (int i = 0; i < args.size(); i++)
61     {
62       String arg = args.get(i);
63       // look for double-dash, e.g. --arg
64       if (arg.startsWith(ArgParser.DOUBLEDASH))
65       {
66         String argName = null;
67         String val = null;
68         Type type = null;
69         // remove "--"
70         argName = arg.substring(ArgParser.DOUBLEDASH.length());
71
72         // look for equals e.g. --arg=value
73         int equalPos = argName.indexOf(ArgParser.EQUALS);
74         if (equalPos > -1)
75         {
76           val = argName.substring(equalPos + 1);
77           argName = argName.substring(0, equalPos);
78         }
79
80         // check for boolean prepended by "no"
81         if (argName.startsWith(ArgParser.NEGATESTRING)
82                 && ArgParser.argMap.containsKey(
83                         argName.substring(ArgParser.NEGATESTRING.length())))
84         {
85           val = "false";
86           argName = argName.substring(ArgParser.NEGATESTRING.length());
87         }
88
89         // look for type modification e.g. --help-opening
90         int dashPos = argName.indexOf(ArgParser.SINGLEDASH);
91         if (dashPos > -1)
92         {
93           String potentialArgName = argName.substring(0, dashPos);
94           Arg potentialArg = ArgParser.argMap.get(potentialArgName);
95           if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE))
96           {
97             String typeName = argName.substring(dashPos + 1);
98             try
99             {
100               type = Type.valueOf(typeName.toUpperCase(Locale.ROOT));
101             } catch (IllegalArgumentException e)
102             {
103               type = Type.INVALID;
104             }
105             argName = argName.substring(0, dashPos);
106           }
107         }
108
109         if (ArgParser.argMap.containsKey(argName) && val == null)
110         {
111           val = "true";
112         }
113
114         Arg a = ArgParser.argMap.get(argName);
115
116         if (a != null)
117         {
118           for (Opt opt : a.getOptions())
119           {
120             if (!argsOptions.contains(opt))
121             {
122               argsOptions.add(opt);
123             }
124           }
125         }
126
127         if (a == null || !a.hasOption(Opt.BOOTSTRAP))
128         {
129           // not a valid bootstrap arg
130           continue;
131         }
132
133         if (a.hasOption(Opt.STRING))
134         {
135           List<String> vals = null;
136           if (equalPos == -1)
137           {
138             vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1);
139           }
140           else
141           {
142             if (a.hasOption(Opt.GLOB))
143             {
144               vals = FileUtils.getFilenamesFromGlob(val);
145             }
146             else
147             {
148               vals = new ArrayList<>();
149               vals.add(val);
150             }
151           }
152           addAll(a, type, vals);
153
154           if (a == Arg.ARGFILE)
155           {
156             for (String filename : vals)
157             {
158               File argFile = new File(filename);
159               parse(ArgParser.readArgFile(argFile), argFile);
160             }
161           }
162         }
163         else
164         {
165           add(a, type, val);
166         }
167       }
168     }
169   }
170
171   public boolean contains(Arg a)
172   {
173     return bootstrapArgMap.containsKey(a);
174   }
175
176   public boolean containsType(Type t)
177   {
178     for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values())
179     {
180       for (Map.Entry<Type, String> e : l)
181       {
182         if (e.getKey() == t)
183           return true;
184       }
185     }
186     return false;
187   }
188
189   public List<Arg> getArgsOfType(Type t)
190   {
191     return getArgsOfType(t, new Opt[] {});
192   }
193
194   public List<Arg> getArgsOfType(Type t, Opt... opts)
195   {
196     List<Arg> args = new ArrayList<>();
197     for (Arg a : bootstrapArgMap.keySet())
198     {
199       if (!a.hasAllOptions(opts))
200         continue;
201
202       List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a);
203       if (l.stream().anyMatch(e -> e.getKey() == t))
204       {
205         args.add(a);
206       }
207     }
208     return args;
209   }
210
211   public List<Map.Entry<Type, String>> getList(Arg a)
212   {
213     return bootstrapArgMap.get(a);
214   }
215
216   public List<String> getValueList(Arg a)
217   {
218     return bootstrapArgMap.get(a).stream().map(e -> e.getValue())
219             .collect(Collectors.toList());
220   }
221
222   private List<Map.Entry<Type, String>> getOrCreateList(Arg a)
223   {
224     List<Map.Entry<Type, String>> l = getList(a);
225     if (l == null)
226     {
227       l = new ArrayList<>();
228       putList(a, l);
229     }
230     return l;
231   }
232
233   private void putList(Arg a, List<Map.Entry<Type, String>> l)
234   {
235     bootstrapArgMap.put(a, l);
236   }
237
238   /*
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
242    * retained).
243    */
244   private void add(Arg a, Type t, String s)
245   {
246     List<Map.Entry<Type, String>> l = getOrCreateList(a);
247     if (a.hasOption(Opt.MULTI) || l.size() == 0)
248     {
249       l.add(entry(t, s));
250     }
251   }
252
253   private void addAll(Arg a, Type t, List<String> al)
254   {
255     List<Map.Entry<Type, String>> l = getOrCreateList(a);
256     if (a.hasOption(Opt.MULTI))
257     {
258       for (String s : al)
259       {
260         l.add(entry(t, s));
261       }
262     }
263     else if (l.size() == 0 && al.size() > 0)
264     {
265       l.add(entry(t, al.get(0)));
266     }
267   }
268
269   private static Map.Entry<Type, String> entry(Type t, String s)
270   {
271     return new AbstractMap.SimpleEntry<Type, String>(t, s);
272   }
273
274   /*
275    * Retrieves the first value even if MULTI.
276    * A convenience for non-MULTI args.
277    */
278   public String get(Arg a)
279   {
280     if (!bootstrapArgMap.containsKey(a))
281       return null;
282     List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a);
283     return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue();
284   }
285
286   public boolean getBoolean(Arg a, boolean d)
287   {
288     if (!bootstrapArgMap.containsKey(a))
289       return d;
290     return Boolean.parseBoolean(get(a));
291   }
292
293   public boolean getBoolean(Arg a)
294   {
295     if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
296     {
297       return false;
298     }
299     if (bootstrapArgMap.containsKey(a))
300     {
301       return Boolean.parseBoolean(get(a));
302     }
303     else
304     {
305       return a.getDefaultBoolValue();
306     }
307   }
308
309   public boolean argsHaveOption(Opt opt)
310   {
311     return argsOptions.contains(opt);
312   }
313 }