Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
BootstrapArgs | 40 | 136 | 79 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.bin.argparser; | |
22 | ||
23 | import java.io.File; | |
24 | import java.util.AbstractMap; | |
25 | import java.util.ArrayList; | |
26 | import java.util.Arrays; | |
27 | import java.util.EnumSet; | |
28 | import java.util.HashMap; | |
29 | import java.util.HashSet; | |
30 | import java.util.List; | |
31 | import java.util.Locale; | |
32 | import java.util.Map; | |
33 | import java.util.Set; | |
34 | import java.util.stream.Collectors; | |
35 | ||
36 | import jalview.bin.argparser.Arg.Opt; | |
37 | import jalview.bin.argparser.Arg.Type; | |
38 | import jalview.util.FileUtils; | |
39 | ||
40 | public class BootstrapArgs | |
41 | { | |
42 | // only need one | |
43 | private Map<Arg, List<Map.Entry<Type, String>>> bootstrapArgMap = new HashMap<>(); | |
44 | ||
45 | private Set<File> argFiles = new HashSet<>(); | |
46 | ||
47 | private Set<Opt> argsOptions = new HashSet<>(); | |
48 | ||
49 | private Set<Type> argsTypes = new HashSet<>(); | |
50 | ||
51 | private boolean outputToStdout = false; | |
52 | ||
53 | 156 | public static BootstrapArgs getBootstrapArgs(String[] args) |
54 | { | |
55 | 156 | List<String> argList = new ArrayList<>(Arrays.asList(args)); |
56 | 156 | return new BootstrapArgs(argList); |
57 | } | |
58 | ||
59 | 46 | public static BootstrapArgs getBootstrapArgs(List<String> args) |
60 | { | |
61 | 46 | return new BootstrapArgs(args); |
62 | } | |
63 | ||
64 | 202 | private BootstrapArgs(List<String> args) |
65 | { | |
66 | 202 | parse(args, null); |
67 | } | |
68 | ||
69 | 255 | private void parse(List<String> args, File inArgFile) |
70 | { | |
71 | 255 | if (args == null) |
72 | 0 | return; |
73 | // avoid looping argFiles | |
74 | 255 | if (inArgFile != null) |
75 | { | |
76 | 53 | if (argFiles.contains(inArgFile)) |
77 | { | |
78 | 0 | jalview.bin.Console.errPrintln( |
79 | "Looped argfiles detected: '" + inArgFile.getPath() + "'"); | |
80 | 0 | return; |
81 | } | |
82 | 53 | argFiles.add(inArgFile); |
83 | } | |
84 | ||
85 | 1488 | for (int i = 0; i < args.size(); i++) |
86 | { | |
87 | 1233 | String arg = args.get(i); |
88 | // look for double-dash, e.g. --arg | |
89 | 1233 | if (arg.startsWith(ArgParser.DOUBLEDASH)) |
90 | { | |
91 | 996 | String argName = null; |
92 | 996 | String val = null; |
93 | 996 | Type type = null; |
94 | // remove "--" | |
95 | 996 | argName = arg.substring(ArgParser.DOUBLEDASH.length()); |
96 | ||
97 | // look for equals e.g. --arg=value | |
98 | 996 | int equalPos = argName.indexOf(ArgParser.EQUALS); |
99 | 996 | if (equalPos > -1) |
100 | { | |
101 | 471 | val = argName.substring(equalPos + 1); |
102 | 471 | argName = argName.substring(0, equalPos); |
103 | } | |
104 | ||
105 | // check for boolean prepended by "no" | |
106 | 996 | if (argName.startsWith(ArgParser.NEGATESTRING) |
107 | && ArgParser.argMap.containsKey( | |
108 | argName.substring(ArgParser.NEGATESTRING.length()))) | |
109 | { | |
110 | 124 | val = "false"; |
111 | 124 | argName = argName.substring(ArgParser.NEGATESTRING.length()); |
112 | } | |
113 | ||
114 | // look for type modification e.g. --help-opening | |
115 | 996 | int dashPos = argName.indexOf(ArgParser.SINGLEDASH); |
116 | 996 | if (dashPos > -1) |
117 | { | |
118 | 43 | String potentialArgName = argName.substring(0, dashPos); |
119 | 43 | Arg potentialArg = ArgParser.argMap.get(potentialArgName); |
120 | 43 | if (potentialArg != null && potentialArg.hasOption(Opt.HASTYPE)) |
121 | { | |
122 | 3 | String typeName = argName.substring(dashPos + 1); |
123 | 3 | try |
124 | { | |
125 | 3 | type = Type.valueOf(typeName.toUpperCase(Locale.ROOT)); |
126 | } catch (IllegalArgumentException e) | |
127 | { | |
128 | 0 | type = Type.INVALID; |
129 | } | |
130 | 3 | argName = argName.substring(0, dashPos); |
131 | } | |
132 | } | |
133 | ||
134 | // after all other args, look for Opt.PREFIX args if still not found | |
135 | 996 | if (!ArgParser.argMap.containsKey(argName)) |
136 | { | |
137 | 116 | for (Arg potentialArg : EnumSet.allOf(Arg.class)) |
138 | { | |
139 | 7888 | if (potentialArg.hasOption(Opt.PREFIXKEV) && argName != null |
140 | && argName.startsWith(potentialArg.getName()) | |
141 | && val != null) | |
142 | { | |
143 | 0 | val = argName.substring(potentialArg.getName().length()) |
144 | + ArgParser.EQUALS + val; | |
145 | 0 | argName = argName.substring(0, |
146 | potentialArg.getName().length()); | |
147 | 0 | break; |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | 996 | Arg a = ArgParser.argMap.get(argName); |
153 | ||
154 | 996 | if (a != null) |
155 | { | |
156 | 880 | for (Opt opt : a.getOptions()) |
157 | { | |
158 | 4133 | if (!argsOptions.contains(opt)) |
159 | { | |
160 | 1744 | argsOptions.add(opt); |
161 | } | |
162 | } | |
163 | 880 | for (Type t : a.getTypes()) |
164 | { | |
165 | 903 | if (!argsTypes.contains(t)) |
166 | { | |
167 | 428 | argsTypes.add(t); |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | 996 | if (a == null || !a.hasOption(Opt.BOOTSTRAP)) |
173 | { | |
174 | // not a bootstrap arg | |
175 | ||
176 | // make a check for an output going to stdout | |
177 | 695 | if (a != null && a.hasOption(Opt.OUTPUTFILE) |
178 | && a.hasOption(Opt.STDOUT)) | |
179 | { | |
180 | 39 | if (val == null && i + 1 < args.size()) |
181 | { | |
182 | 25 | val = args.get(i + 1); |
183 | } | |
184 | 39 | if (val.startsWith("[") && val.indexOf(']') > 0) |
185 | { | |
186 | 1 | val = val.substring(val.indexOf(']') + 1); |
187 | } | |
188 | ||
189 | 39 | if (ArgParser.STDOUTFILENAME.equals(val)) |
190 | { | |
191 | 8 | this.outputToStdout = true; |
192 | } | |
193 | } | |
194 | ||
195 | 695 | continue; |
196 | } | |
197 | ||
198 | 301 | if (a.hasOption(Opt.STRING)) |
199 | { | |
200 | 61 | List<String> vals = null; |
201 | 61 | if (equalPos == -1) |
202 | { | |
203 | 29 | vals = ArgParser.getShellGlobbedFilenameValues(a, args, i + 1); |
204 | } | |
205 | else | |
206 | { | |
207 | 32 | if (a.hasOption(Opt.GLOB)) |
208 | { | |
209 | 15 | vals = FileUtils.getFilenamesFromGlob(val); |
210 | } | |
211 | else | |
212 | { | |
213 | 17 | vals = new ArrayList<>(); |
214 | 17 | vals.add(val); |
215 | } | |
216 | } | |
217 | 61 | addAll(a, type, vals); |
218 | ||
219 | 61 | if (a == Arg.ARGFILE) |
220 | { | |
221 | 23 | for (String filename : vals) |
222 | { | |
223 | 53 | File argFile = new File(filename); |
224 | 53 | parse(ArgParser.readArgFile(argFile), argFile); |
225 | } | |
226 | } | |
227 | } | |
228 | else | |
229 | { | |
230 | 240 | if (val == null) |
231 | { | |
232 | 144 | val = "true"; |
233 | } | |
234 | ||
235 | 240 | add(a, type, val); |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | // if in an argfile, remove it from the hashset so it can be re-used in | |
241 | // another argfile | |
242 | 255 | if (inArgFile != null) |
243 | { | |
244 | 53 | argFiles.remove(inArgFile); |
245 | } | |
246 | } | |
247 | ||
248 | 1824 | public boolean contains(Arg a) |
249 | { | |
250 | 1824 | return bootstrapArgMap.containsKey(a); |
251 | } | |
252 | ||
253 | 0 | public boolean containsType(Type t) |
254 | { | |
255 | 0 | for (List<Map.Entry<Type, String>> l : bootstrapArgMap.values()) |
256 | { | |
257 | 0 | for (Map.Entry<Type, String> e : l) |
258 | { | |
259 | 0 | if (e.getKey() == t) |
260 | 0 | return true; |
261 | } | |
262 | } | |
263 | 0 | return false; |
264 | } | |
265 | ||
266 | 0 | public List<Arg> getArgsOfType(Type t) |
267 | { | |
268 | 0 | return getArgsOfType(t, new Opt[] {}); |
269 | } | |
270 | ||
271 | 0 | public List<Arg> getArgsOfType(Type t, Opt... opts) |
272 | { | |
273 | 0 | List<Arg> args = new ArrayList<>(); |
274 | 0 | for (Arg a : bootstrapArgMap.keySet()) |
275 | { | |
276 | 0 | if (!a.hasAllOptions(opts)) |
277 | 0 | continue; |
278 | ||
279 | 0 | List<Map.Entry<Type, String>> l = bootstrapArgMap.get(a); |
280 | 0 | if (l.stream().anyMatch(e -> e.getKey() == t)) |
281 | { | |
282 | 0 | args.add(a); |
283 | } | |
284 | } | |
285 | 0 | return args; |
286 | } | |
287 | ||
288 | 301 | public List<Map.Entry<Type, String>> getList(Arg a) |
289 | { | |
290 | 301 | return bootstrapArgMap.get(a); |
291 | } | |
292 | ||
293 | 15 | public List<String> getValueList(Arg a) |
294 | { | |
295 | 15 | return bootstrapArgMap.get(a).stream().map(e -> e.getValue()) |
296 | .collect(Collectors.toList()); | |
297 | } | |
298 | ||
299 | 301 | private List<Map.Entry<Type, String>> getOrCreateList(Arg a) |
300 | { | |
301 | 301 | List<Map.Entry<Type, String>> l = getList(a); |
302 | 301 | if (l == null) |
303 | { | |
304 | 280 | l = new ArrayList<>(); |
305 | 280 | putList(a, l); |
306 | } | |
307 | 301 | return l; |
308 | } | |
309 | ||
310 | 280 | private void putList(Arg a, List<Map.Entry<Type, String>> l) |
311 | { | |
312 | 280 | bootstrapArgMap.put(a, l); |
313 | } | |
314 | ||
315 | /* | |
316 | * Creates a new list if not used before, | |
317 | * adds the value unless the existing list is non-empty | |
318 | * and the arg is not MULTI (so first expressed value is | |
319 | * retained). | |
320 | */ | |
321 | 240 | private void add(Arg a, Type t, String s) |
322 | { | |
323 | 240 | List<Map.Entry<Type, String>> l = getOrCreateList(a); |
324 | 240 | if (a.hasOption(Opt.MULTIVALUE) || l.size() == 0) |
325 | { | |
326 | 219 | l.add(entry(t, s)); |
327 | } | |
328 | } | |
329 | ||
330 | 61 | private void addAll(Arg a, Type t, List<String> al) |
331 | { | |
332 | 61 | List<Map.Entry<Type, String>> l = getOrCreateList(a); |
333 | 61 | if (a.hasOption(Opt.MULTIVALUE)) |
334 | { | |
335 | 23 | for (String s : al) |
336 | { | |
337 | 53 | l.add(entry(t, s)); |
338 | } | |
339 | } | |
340 | 38 | else if (l.size() == 0 && al.size() > 0) |
341 | { | |
342 | 38 | l.add(entry(t, al.get(0))); |
343 | } | |
344 | } | |
345 | ||
346 | 310 | private static Map.Entry<Type, String> entry(Type t, String s) |
347 | { | |
348 | 310 | return new AbstractMap.SimpleEntry<Type, String>(t, s); |
349 | } | |
350 | ||
351 | /* | |
352 | * Retrieves the first value even if MULTI. | |
353 | * A convenience for non-MULTI args. | |
354 | */ | |
355 | 617 | public String getValue(Arg a) |
356 | { | |
357 | 617 | if (!bootstrapArgMap.containsKey(a)) |
358 | 331 | return null; |
359 | 286 | List<Map.Entry<Type, String>> aL = bootstrapArgMap.get(a); |
360 | 286 | return (aL == null || aL.size() == 0) ? null : aL.get(0).getValue(); |
361 | } | |
362 | ||
363 | 0 | public boolean getBoolean(Arg a, boolean d) |
364 | { | |
365 | 0 | if (!bootstrapArgMap.containsKey(a)) |
366 | 0 | return d; |
367 | 0 | return Boolean.parseBoolean(getValue(a)); |
368 | } | |
369 | ||
370 | 719 | public boolean getBoolean(Arg a) |
371 | { | |
372 | 719 | if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY))) |
373 | { | |
374 | 0 | return false; |
375 | } | |
376 | 719 | if (bootstrapArgMap.containsKey(a)) |
377 | { | |
378 | 142 | return Boolean.parseBoolean(getValue(a)); |
379 | } | |
380 | else | |
381 | { | |
382 | 577 | return a.getDefaultBoolValue(); |
383 | } | |
384 | } | |
385 | ||
386 | 65 | public boolean argsHaveOption(Opt opt) |
387 | { | |
388 | 65 | return argsOptions.contains(opt); |
389 | } | |
390 | ||
391 | 144 | public boolean argsHaveType(Type type) |
392 | { | |
393 | 144 | return argsTypes.contains(type); |
394 | } | |
395 | ||
396 | 144 | public boolean isHeadless() |
397 | { | |
398 | 144 | boolean isHeadless = false; |
399 | 144 | if (this.argsHaveType(Type.HELP)) |
400 | { | |
401 | // --help, --help-all, ... specified => headless | |
402 | 4 | isHeadless = true; |
403 | } | |
404 | 140 | else if (this.contains(Arg.VERSION)) |
405 | { | |
406 | // --version specified => headless | |
407 | 0 | isHeadless = true; |
408 | } | |
409 | 140 | else if (this.contains(Arg.GUI)) |
410 | { | |
411 | // --gui specified => forced NOT headless | |
412 | 50 | isHeadless = !this.getBoolean(Arg.GUI); |
413 | } | |
414 | 90 | else if (this.contains(Arg.HEADLESS)) |
415 | { | |
416 | // --headless has been specified on the command line => headless | |
417 | 25 | isHeadless = this.getBoolean(Arg.HEADLESS); |
418 | } | |
419 | 65 | else if (this.argsHaveOption(Opt.OUTPUTFILE)) |
420 | { | |
421 | // --output file.fa, --image pic.png, --structureimage struct.png => | |
422 | // assume headless unless above has been already specified | |
423 | 12 | isHeadless = true; |
424 | } | |
425 | 144 | return isHeadless; |
426 | } | |
427 | ||
428 | 551 | public boolean outputToStdout() |
429 | { | |
430 | 551 | return this.outputToStdout; |
431 | } | |
432 | } |