2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.bin.argparser;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
32 import jalview.bin.Cache;
33 import jalview.bin.Console;
34 import jalview.bin.argparser.Arg.Opt;
35 import jalview.bin.argparser.Arg.Type;
36 import jalview.util.FileUtils;
39 * Helper class to allow easy extraction of information about specific argument
40 * values (without having to check for null etc all the time)
42 public class ArgValuesMap
44 private List<ArgInfo> argInfoList = new ArrayList<>();
46 protected Map<Arg, ArgValues> m;
48 private String linkedId;
50 protected ArgValuesMap(String linkedId)
52 this.linkedId = linkedId;
56 protected ArgValuesMap(String linkedId, Map<Arg, ArgValues> map)
58 this.linkedId = linkedId;
62 public String getLinkedId()
67 private Map<Arg, ArgValues> getMap()
74 m = new HashMap<Arg, ArgValues>();
77 private void newArg(Arg a)
82 m.put(a, new ArgValues(a));
85 public ArgValues getArgValues(Arg a)
87 return m == null ? null : m.get(a);
90 public ArgValues getOrCreateArgValues(Arg a)
92 ArgValues avs = m.get(a);
95 return getArgValues(a);
98 public List<ArgValue> getArgValueList(Arg a)
100 ArgValues avs = getArgValues(a);
101 return avs == null ? new ArrayList<>() : avs.getArgValueList();
104 public List<ArgValue> getArgValueListFromSubValOrArg(ArgValue av, Arg a,
107 return getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes(
108 null, a, Position.AFTER, av, sv, null, null, null, true);
111 public List<ArgValue> getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes(
112 ArgParser ap, Arg a, ArgValuesMap.Position pos, ArgValue av,
113 SubVals sv, String key, String pref, String def,
118 List<ArgValue> avList = new ArrayList<>();
119 if (sv != null && sv.has(key) && sv.get(key) != null)
121 String value = ap == null ? sv.get(key)
122 : sv.getWithSubstitutions(ap, getLinkedId(), key);
123 // protected ArgValue(Arg a, SubVals sv, Type type, String content, int
126 avList.add(new ArgValue(a, null, null, value, av.getArgIndex()));
128 else if (containsArg(a))
130 if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null)
131 avList.add(getArgValue(a));
132 else if (pos == ArgValuesMap.Position.BEFORE
133 && getClosestPreviousArgValueOfArg(av, a) != null)
135 for (ArgValue tmpAv : getArgValues(a).getArgValueList())
137 if (tmpAv.getArgIndex() >= av.getArgIndex())
144 else if (pos == ArgValuesMap.Position.AFTER
145 && getClosestNextArgValueOfArg(av, a, withinTypes) != null)
147 for (ArgValue tmpAv : getArgValues(a).getArgValueList())
149 if (tmpAv.getArgIndex() <= av.getArgIndex())
158 // check if withinType the avs don't belong to the next primary arg
159 // of this type. Checking for *any* shared type.
160 if (withinTypes && !avList.isEmpty())
162 int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE;
163 // run through every Arg used in this ArgValuesMap
164 for (Arg tmpA : this.getArgKeys())
166 // only interested in Opt.PRIMARY args of the same type
167 if (tmpA.sharesType(a) && tmpA.hasOption(Opt.PRIMARY))
169 for (ArgValue tmpAv : getArgValueList(tmpA))
171 int tmpArgIndex = tmpAv.getArgIndex();
172 if (tmpArgIndex > av.getArgIndex()
173 && tmpArgIndex < nextPrimaryArgOfSameTypeIndex)
175 nextPrimaryArgOfSameTypeIndex = tmpArgIndex;
180 List<ArgValue> tmpList = List.copyOf(avList);
181 for (ArgValue tmpAv : tmpList)
183 if (nextPrimaryArgOfSameTypeIndex < tmpAv.getArgIndex())
185 // looks like this tmpAv actually belongs to a different primary Arg
186 avList.remove(tmpAv);
194 public ArgValue getArgValue(Arg a)
196 List<ArgValue> vals = getArgValueList(a);
197 return (vals == null || vals.size() == 0) ? null : vals.get(0);
200 public String getValue(Arg a)
202 ArgValue av = getArgValue(a);
203 return av == null ? null : av.getValue();
206 public boolean containsArg(Arg a)
208 if (m == null || !m.containsKey(a))
210 return a.hasOption(Opt.STRING) ? getArgValue(a) != null : true;
213 public boolean hasValue(Arg a, String val)
215 if (m == null || !m.containsKey(a))
217 for (ArgValue av : getArgValueList(a))
219 String avVal = av.getValue();
220 if ((val == null && avVal == null)
221 || (val != null && val.equals(avVal)))
229 public boolean getBoolean(Arg a)
231 ArgValues av = getArgValues(a);
232 return av == null ? false : av.getBoolean();
235 public Set<Arg> getArgKeys()
240 public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey)
242 return getArgValueOfArgWithSubValKey(a, svKey, false);
245 public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey,
248 ArgValues avs = this.getArgValues(a);
253 List<ArgValue> compareAvs = avs.getArgValueList();
254 for (int i = 0; i < compareAvs.size(); i++)
256 int index = last ? compareAvs.size() - 1 - i : i;
257 ArgValue av = compareAvs.get(index);
258 SubVals sv = av.getSubVals();
259 if (sv.has(svKey) && !sv.get(svKey).equals("false"))
267 public ArgValue getClosestPreviousArgValueOfArg(ArgValue thisAv, Arg a)
269 ArgValue closestAv = null;
270 int thisArgIndex = thisAv.getArgIndex();
271 ArgValues compareAvs = this.getArgValues(a);
272 int closestPreviousIndex = -1;
273 for (ArgValue av : compareAvs.getArgValueList())
275 int argIndex = av.getArgIndex();
276 if (argIndex < thisArgIndex && argIndex > closestPreviousIndex)
278 closestPreviousIndex = argIndex;
285 public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a,
288 // this looks for the *next* arg that *might* be referring back to
289 // a thisAv. Such an arg would have no subValues (if it does it should
290 // specify an id in the subValues so wouldn't need to be guessed).
291 ArgValue closestAv = null;
292 int thisArgIndex = thisAv.getArgIndex();
295 ArgValues compareAvs = this.getArgValues(a);
296 int closestNextIndex = Integer.MAX_VALUE;
297 for (ArgValue av : compareAvs.getArgValueList())
299 int argIndex = av.getArgIndex();
300 if (argIndex > thisArgIndex && argIndex < closestNextIndex)
302 closestNextIndex = argIndex;
307 // check if withinType this closestAv doesn't belong to the next primary arg
308 // of this type. Checking for *any* shared type.
309 if (withinTypes && closestAv != null)
311 int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE;
312 for (Arg tmpA : this.getArgKeys())
314 // interested in Opt.PRIMARY args of the same type
315 if (tmpA.sharesType(a) && tmpA.hasOption(Opt.PRIMARY))
317 for (ArgValue tmpAv : getArgValueList(tmpA))
319 int tmpArgIndex = tmpAv.getArgIndex();
320 if (tmpArgIndex > thisArgIndex
321 && tmpArgIndex < nextPrimaryArgOfSameTypeIndex)
323 nextPrimaryArgOfSameTypeIndex = tmpArgIndex;
328 if (nextPrimaryArgOfSameTypeIndex < closestAv.getArgIndex())
330 // looks like closestAv actually belongs to a different primary Arg
338 // TODO this is incomplete and currently unused (fortunately)
339 public ArgValue[] getArgValuesReferringTo(String key, String value, Arg a)
341 // this looks for the *next* arg that *might* be referring back to
342 // a thisAv. Such an arg would have no subValues (if it does it should
343 // specify an id in the subValues so wouldn't need to be guessed).
344 List<ArgValue> avList = new ArrayList<>();
345 Arg[] args = a == null ? (Arg[]) this.getMap().keySet().toArray()
348 for (Arg keyArg : args)
350 for (ArgValue av : this.getArgValueList(keyArg))
355 return (ArgValue[]) avList.toArray();
358 public boolean hasId(Arg a, String id)
360 ArgValues avs = this.getArgValues(a);
361 return avs == null ? false : avs.hasId(id);
364 public ArgValue getId(Arg a, String id)
366 ArgValues avs = this.getArgValues(a);
367 return avs == null ? null : avs.getId(id);
371 * This method returns the basename of the first --append or --open value.
372 * Used primarily for substitutions in output filenames.
374 public String getBasename()
376 return getDirBasenameOrExtension(false, false, false);
380 * This method returns the basename of the first --append or --open value.
381 * Used primarily for substitutions in output filenames.
383 public String getExtension()
385 return getDirBasenameOrExtension(false, true, false);
389 * This method returns the dirname of the first --append or --open value.
390 * Used primarily for substitutions in output filenames.
392 public String getDirname()
394 return getDirBasenameOrExtension(true, false, false);
397 public String getDirBasenameOrExtension(boolean dirname,
398 boolean extension, boolean absoluteDirname)
400 String filename = null;
401 String appendVal = getValue(Arg.APPEND);
402 String openVal = getValue(Arg.OPEN);
403 if (appendVal != null)
404 filename = appendVal;
405 if (filename == null && openVal != null)
407 if (filename == null)
410 File file = new File(filename);
413 return FileUtils.getDirname(file);
415 return extension ? FileUtils.getExtension(file)
416 : FileUtils.getBasename(file);
420 * Checks if there is an Arg with Opt
422 public boolean hasArgWithOption(Opt o)
424 for (Arg a : getArgKeys())
433 * ArgInfo is a more straightforward list of arguments and their info
436 public void addArgInfo(Arg arg, String value, SubVals subVals,
439 argInfoList.add(new ArgInfo(arg, value, subVals, argIndex));
442 public List<ArgInfo> getArgInfoList()
444 Collections.sort(argInfoList);
449 * get from following Arg of type a or subval of same name (lowercase)
451 public String getValueFromSubValOrArg(ArgValue av, Arg a, SubVals sv)
453 return getFromSubValArgOrPref(av, a, sv, null, null, null);
457 * get from following Arg of type a or subval key or preference pref or
460 public String getFromSubValArgOrPref(ArgValue av, Arg a, SubVals sv,
461 String key, String pref, String def)
463 return getFromSubValArgOrPref(a, Position.AFTER, av, sv, key, pref,
468 * get from following(AFTER), first occurence of (FIRST) or previous (BEFORE)
469 * Arg of type a or subval key or preference pref or default def
471 public String getFromSubValArgOrPref(Arg a, Position pos, ArgValue av,
472 SubVals sv, String key, String pref, String def)
474 return getFromSubValArgOrPrefWithSubstitutions(null, a, pos, av, sv,
478 public String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap, Arg a,
479 Position pos, ArgValue av, SubVals sv, String key, String pref,
482 return getFromSubValArgOrPrefWithSubstitutionsWithinTypes(ap, a, pos,
483 av, sv, key, pref, def, true);
486 public String getFromSubValArgOrPrefWithSubstitutionsWithinTypes(
487 ArgParser ap, Arg a, Position pos, ArgValue av, SubVals sv,
488 String key, String pref, String def, boolean withinTypes)
493 if (sv != null && sv.has(key) && sv.get(key) != null)
495 value = ap == null ? sv.get(key)
496 : sv.getWithSubstitutions(ap, getLinkedId(), key);
498 else if (containsArg(a))
500 if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null)
502 else if (pos == ArgValuesMap.Position.BEFORE
503 && getClosestPreviousArgValueOfArg(av, a) != null)
504 value = getClosestPreviousArgValueOfArg(av, a).getValue();
505 else if (pos == ArgValuesMap.Position.AFTER
506 && getClosestNextArgValueOfArg(av, a, withinTypes) != null)
507 value = getClosestNextArgValueOfArg(av, a, withinTypes).getValue();
509 // look for allstructures subval for Type.STRUCTURE
510 Arg arg = av.getArg();
511 if (value == null && arg.hasOption(Opt.PRIMARY)
512 && arg.hasType(Type.STRUCTURE) && !a.hasOption(Opt.PRIMARY)
513 && (a.getFirstType() == Type.STRUCTURE
514 // || a.getType() == Type.STRUCTUREIMAGE))
517 ArgValue av2 = getArgValueOfArgWithSubValKey(a,
518 Arg.ALLSTRUCTURES.getName());
521 value = av2.getValue();
527 value = pref != null ? Cache.getDefault(pref, def) : def;
532 public boolean getBoolFromSubValOrArg(Arg a, SubVals sv)
534 return getFromSubValArgOrPref(a, sv, null, null, false);
537 public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
538 String pref, boolean def)
540 return getFromSubValArgOrPref(a, sv, key, pref, def, false);
543 public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key,
544 String pref, boolean def, boolean invertPref)
546 if ((key == null && a == null) || (sv == null && a == null))
549 boolean usingArgKey = false;
556 String nokey = ArgParser.NEGATESTRING + key;
558 // look for key or nokey in subvals first (if using Arg check options)
561 // check for true boolean
562 if (sv.has(key) && sv.get(key) != null)
566 if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY)))
569 "Looking for boolean in subval from non-boolean/non-unary Arg "
574 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
577 // check for negative boolean (subval "no..." will be "true")
578 if (sv.has(nokey) && sv.get(nokey) != null)
582 if (!(a.hasOption(Opt.BOOLEAN)))
585 "Looking for negative boolean in subval from non-boolean Arg "
590 return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true");
596 return getBoolean(a);
598 // return preference or default
599 boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false;
600 return pref != null ? (invertPref ? !prefVal : prefVal) : def;
603 public class ArgInfo implements Comparable<ArgInfo>
607 private String value;
609 private SubVals subVals;
611 private int argIndex;
613 public ArgInfo(Arg arg, String value, SubVals subVals, int argIndex)
617 this.subVals = subVals;
618 this.argIndex = argIndex;
626 public String value()
631 public SubVals subVals()
636 public int argIndex()
642 public int compareTo(ArgInfo ai2)
644 return Integer.compare(this.argIndex(), ai2.argIndex());
648 public static enum Position