/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.bin.argparser; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; 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 jalview.bin.Cache; import jalview.bin.Console; import jalview.bin.argparser.Arg.Opt; import jalview.bin.argparser.Arg.Type; import jalview.util.FileUtils; /** * Helper class to allow easy extraction of information about specific argument * values (without having to check for null etc all the time) */ public class ArgValuesMap { private List argInfoList = new ArrayList<>(); protected Map m; private String linkedId; protected ArgValuesMap(String linkedId) { this.linkedId = linkedId; this.newMap(); } protected ArgValuesMap(String linkedId, Map map) { this.linkedId = linkedId; this.m = map; } public String getLinkedId() { return linkedId; } private Map getMap() { return m; } private void newMap() { m = new HashMap(); } private void newArg(Arg a) { if (m == null) { newMap(); } if (!containsArg(a)) { m.put(a, new ArgValues(a, this)); } } public ArgValues getArgValues(Arg a) { return m == null ? null : m.get(a); } public ArgValues getOrCreateArgValues(Arg a) { ArgValues avs = m.get(a); if (avs == null) newArg(a); return getArgValues(a); } public List getArgValueList(Arg a) { ArgValues avs = getArgValues(a); return avs == null ? new ArrayList<>() : avs.getArgValueList(); } public List getArgValueListFromSubValOrArg(ArgValue av, Arg a, SubVals sv) { return getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes( null, a, Position.AFTER, av, sv, null, null, null, true, null); } public List getArgValueListFromSubValArgOrPrefWithSubstitutionsWithinTypes( ArgParser ap, Arg a, ArgValuesMap.Position pos, ArgValue av, SubVals sv, String key, String pref, String def, boolean withinTypes, Type type) { if (key == null) { key = a.getName(); } Set types = new HashSet<>(); if (type == null) { types.addAll(Arrays.asList(av.getArg().getTypes())); } else { types.add(type); } List avList = new ArrayList<>(); if (sv != null && sv.has(key) && sv.get(key) != null) { String value = ap == null ? sv.get(key) : sv.getWithSubstitutions(ap, getLinkedId(), key); // protected ArgValue(Arg a, SubVals sv, Type type, String content, int // argIndex) ArgValue svav = new ArgValue(a, null, null, value, av.getArgIndex(), false, null, this.getLinkedId()); avList.add(svav); } else if (containsArg(a)) { if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null) avList.add(getArgValue(a)); else if (pos == ArgValuesMap.Position.BEFORE && getClosestPreviousArgValueOfArg(av, a) != null) { for (ArgValue tmpAv : getArgValues(a).getArgValueList()) { if (tmpAv.getArgIndex() >= av.getArgIndex()) { continue; } avList.add(tmpAv); } } else if (pos == ArgValuesMap.Position.AFTER && getClosestNextArgValueOfArg(av, a, withinTypes) != null) { for (ArgValue tmpAv : getArgValues(a).getArgValueList()) { if (tmpAv.getArgIndex() <= av.getArgIndex()) { continue; } avList.add(tmpAv); } } } // check if withinType the avs don't belong to the next primary arg // of this type. Checking for *any* shared type. if (withinTypes && !avList.isEmpty()) { int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE; // run through every Arg used in this ArgValuesMap for (Arg tmpA : this.getArgKeys()) { // only interested in looking up to next Opt.PRIMARY args of the same // type as av (or provided type) if (tmpA.hasType(types) && tmpA.hasOption(Opt.PRIMARY)) { for (ArgValue tmpAv : getArgValueList(tmpA)) { int tmpArgIndex = tmpAv.getArgIndex(); if (tmpArgIndex > av.getArgIndex() && tmpArgIndex < nextPrimaryArgOfSameTypeIndex) { nextPrimaryArgOfSameTypeIndex = tmpArgIndex; } } } } List tmpList = new ArrayList<>(); for (ArgValue tmpAv : avList) { int tmpAvIndex = tmpAv.getArgIndex(); if (av.getArgIndex() < tmpAvIndex && tmpAvIndex < nextPrimaryArgOfSameTypeIndex) { tmpList.add(tmpAv); } } avList = tmpList; } return avList; } public ArgValue getArgValue(Arg a) { List vals = getArgValueList(a); return (vals == null || vals.size() == 0) ? null : vals.get(0); } public String getValue(Arg a) { ArgValue av = getArgValue(a); return av == null ? null : av.getValue(); } public List getValues(Arg a) { return toValues(getArgValueList(a)); } public static List toValues(List avl) { if (avl == null) { return null; } List vl = new ArrayList<>(); for (ArgValue av : avl) { vl.add(av.getValue()); } return vl; } public boolean containsArg(Arg a) { if (m == null || !m.containsKey(a)) return false; return a.hasOption(Opt.STRING) ? getArgValue(a) != null : true; } public boolean hasValue(Arg a, String val) { if (m == null || !m.containsKey(a)) return false; for (ArgValue av : getArgValueList(a)) { String avVal = av.getValue(); if ((val == null && avVal == null) || (val != null && val.equals(avVal))) { return true; } } return false; } public boolean getBoolean(Arg a) { ArgValues av = getArgValues(a); return av == null ? false : av.getBoolean(); } public Set getArgKeys() { return m.keySet(); } public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey) { return getArgValueOfArgWithSubValKey(a, svKey, false); } public ArgValue getArgValueOfArgWithSubValKey(Arg a, String svKey, boolean last) { ArgValues avs = this.getArgValues(a); if (avs == null) { return null; } List compareAvs = avs.getArgValueList(); for (int i = 0; i < compareAvs.size(); i++) { int index = last ? compareAvs.size() - 1 - i : i; ArgValue av = compareAvs.get(index); SubVals sv = av.getSubVals(); if (sv.has(svKey) && !sv.get(svKey).equals("false")) { return av; } } return null; } public ArgValue getClosestPreviousArgValueOfArg(ArgValue thisAv, Arg a) { ArgValue closestAv = null; int thisArgIndex = thisAv.getArgIndex(); ArgValues compareAvs = this.getArgValues(a); int closestPreviousIndex = -1; for (ArgValue av : compareAvs.getArgValueList()) { int argIndex = av.getArgIndex(); if (argIndex < thisArgIndex && argIndex > closestPreviousIndex) { closestPreviousIndex = argIndex; closestAv = av; } } return closestAv; } public ArgValue getClosestNextArgValueOfArg(ArgValue thisAv, Arg a, boolean withinTypes) { // this looks for the *next* arg that *might* be referring back to // a thisAv. Such an arg would have no subValues (if it does it should // specify an id in the subValues so wouldn't need to be guessed). ArgValue closestAv = null; int thisArgIndex = thisAv.getArgIndex(); if (!containsArg(a)) return null; ArgValues compareAvs = this.getArgValues(a); int closestNextIndex = Integer.MAX_VALUE; for (ArgValue av : compareAvs.getArgValueList()) { int argIndex = av.getArgIndex(); if (argIndex > thisArgIndex && argIndex < closestNextIndex) { closestNextIndex = argIndex; closestAv = av; } } // check if withinType this closestAv doesn't belong to the next primary arg // of this type. Checking for *any* shared type. if (withinTypes && closestAv != null) { int nextPrimaryArgOfSameTypeIndex = Integer.MAX_VALUE; for (Arg tmpA : this.getArgKeys()) { // interested in Opt.PRIMARY args of the same type if (tmpA.sharesType(a) && tmpA.hasOption(Opt.PRIMARY)) { for (ArgValue tmpAv : getArgValueList(tmpA)) { int tmpArgIndex = tmpAv.getArgIndex(); if (tmpArgIndex > thisArgIndex && tmpArgIndex < nextPrimaryArgOfSameTypeIndex) { nextPrimaryArgOfSameTypeIndex = tmpArgIndex; } } } } if (nextPrimaryArgOfSameTypeIndex < closestAv.getArgIndex()) { // looks like closestAv actually belongs to a different primary Arg return null; } } return closestAv; } // TODO this is incomplete and currently unused (fortunately) public ArgValue[] getArgValuesReferringTo(String key, String value, Arg a) { // this looks for the *next* arg that *might* be referring back to // a thisAv. Such an arg would have no subValues (if it does it should // specify an id in the subValues so wouldn't need to be guessed). List avList = new ArrayList<>(); Arg[] args = a == null ? (Arg[]) this.getMap().keySet().toArray() : new Arg[] { a }; for (Arg keyArg : args) { for (ArgValue av : this.getArgValueList(keyArg)) { } } return (ArgValue[]) avList.toArray(); } public boolean hasId(Arg a, String id) { ArgValues avs = this.getArgValues(a); return avs == null ? false : avs.hasId(id); } public ArgValue getId(Arg a, String id) { ArgValues avs = this.getArgValues(a); return avs == null ? null : avs.getId(id); } /* * This method returns the basename of the first --append or --open value. * Used primarily for substitutions in output filenames. */ public String getBasename() { return getDirBasenameOrExtension(false, false, false); } /* * This method returns the basename of the first --append or --open value. * Used primarily for substitutions in output filenames. */ public String getExtension() { return getDirBasenameOrExtension(false, true, false); } /* * This method returns the dirname of the first --append or --open value. * Used primarily for substitutions in output filenames. */ public String getDirname() { return getDirBasenameOrExtension(true, false, false); } public String getDirBasenameOrExtension(boolean dirname, boolean extension, boolean absoluteDirname) { String filename = null; String appendVal = getValue(Arg.APPEND); String openVal = getValue(Arg.OPEN); if (appendVal != null) filename = appendVal; if (filename == null && openVal != null) filename = openVal; if (filename == null) return null; File file = new File(filename); if (dirname) { return FileUtils.getDirname(file); } return extension ? FileUtils.getExtension(file) : FileUtils.getBasename(file); } /* * Checks if there is an Arg with Opt */ public boolean hasArgWithOption(Opt o) { for (Arg a : getArgKeys()) { if (a.hasOption(o)) return true; } return false; } /* * ArgInfo is a more straightforward list of arguments and their info */ public void addArgInfo(Arg arg, String value, SubVals subVals, int argIndex) { argInfoList.add(new ArgInfo(arg, value, subVals, argIndex)); } public List getArgInfoList() { Collections.sort(argInfoList); return argInfoList; } /** * get from following Arg of type a or subval of same name (lowercase) */ public String getValueFromSubValOrArg(ArgValue av, Arg a, SubVals sv) { return getFromSubValArgOrPref(av, a, sv, null, null, null); } /** * get from following Arg of type a or subval key or preference pref or * default def */ public String getFromSubValArgOrPref(ArgValue av, Arg a, SubVals sv, String key, String pref, String def) { return getFromSubValArgOrPref(a, Position.AFTER, av, sv, key, pref, def); } /** * get from following(AFTER), first occurence of (FIRST) or previous (BEFORE) * Arg of type a or subval key or preference pref or default def */ public String getFromSubValArgOrPref(Arg a, Position pos, ArgValue av, SubVals sv, String key, String pref, String def) { return getFromSubValArgOrPrefWithSubstitutions(null, a, pos, av, sv, key, pref, def); } public String getFromSubValArgOrPrefWithSubstitutions(ArgParser ap, Arg a, Position pos, ArgValue av, SubVals sv, String key, String pref, String def) { return getFromSubValArgOrPrefWithSubstitutionsWithinTypes(ap, a, pos, av, sv, key, pref, def, true); } public String getFromSubValArgOrPrefWithSubstitutionsWithinTypes( ArgParser ap, Arg a, Position pos, ArgValue av, SubVals sv, String key, String pref, String def, boolean withinTypes) { if (key == null) key = a.getName(); String value = null; if (sv != null && sv.has(key) && sv.get(key) != null) { value = ap == null ? sv.get(key) : sv.getWithSubstitutions(ap, getLinkedId(), key); } else if (containsArg(a)) { if (pos == ArgValuesMap.Position.FIRST && getValue(a) != null) value = getValue(a); else if (pos == ArgValuesMap.Position.BEFORE && getClosestPreviousArgValueOfArg(av, a) != null) value = getClosestPreviousArgValueOfArg(av, a).getValue(); else if (pos == ArgValuesMap.Position.AFTER && getClosestNextArgValueOfArg(av, a, withinTypes) != null) value = getClosestNextArgValueOfArg(av, a, withinTypes).getValue(); // look for allstructures subval for Type.STRUCTURE Arg arg = av.getArg(); if (value == null && arg.hasOption(Opt.PRIMARY) && arg.hasType(Type.STRUCTURE) && !a.hasOption(Opt.PRIMARY) && (a.getFirstType() == Type.STRUCTURE // || a.getType() == Type.STRUCTUREIMAGE)) )) { ArgValue av2 = getArgValueOfArgWithSubValKey(a, Arg.ALLSTRUCTURES.getName()); if (av2 != null) { value = av2.getValue(); } } if (value == null) { // look for --all --a occurrences for (ArgValue tmpAv : this.getArgValueList(a)) { if (tmpAv.setByWildcardLinkedId()) { value = tmpAv.getValue(); } } } } if (value == null) { value = pref != null ? Cache.getDefault(pref, def) : def; } return value; } public boolean getBoolFromSubValOrArg(Arg a, SubVals sv) { return getFromSubValArgOrPref(a, sv, null, null, false); } public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key, String pref, boolean def) { return getFromSubValArgOrPref(a, sv, key, pref, def, false); } public boolean getFromSubValArgOrPref(Arg a, SubVals sv, String key, String pref, boolean def, boolean invertPref) { if ((key == null && a == null) || (sv == null && a == null)) return false; boolean usingArgKey = false; if (key == null) { key = a.getName(); usingArgKey = true; } String nokey = ArgParser.NEGATESTRING + key; // look for key or nokey in subvals first (if using Arg check options) if (sv != null) { // check for true boolean if (sv.has(key) && sv.get(key) != null) { if (usingArgKey) { if (!(a.hasOption(Opt.BOOLEAN) || a.hasOption(Opt.UNARY))) { Console.debug( "Looking for boolean in subval from non-boolean/non-unary Arg " + a.getName()); return false; } } return sv.get(key).toLowerCase(Locale.ROOT).equals("true"); } // check for negative boolean (subval "no..." will be "true") if (sv.has(nokey) && sv.get(nokey) != null) { if (usingArgKey) { if (!(a.hasOption(Opt.BOOLEAN))) { Console.debug( "Looking for negative boolean in subval from non-boolean Arg " + a.getName()); return false; } } return !sv.get(nokey).toLowerCase(Locale.ROOT).equals("true"); } } // check argvalues if (containsArg(a)) return getBoolean(a); // return preference or default boolean prefVal = pref != null ? Cache.getDefault(pref, def) : false; return pref != null ? (invertPref ? !prefVal : prefVal) : def; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Arg a : this.getArgKeys()) { sb.append(a.argString()); sb.append(":\n"); for (ArgValue av : this.getArgValueList(a)) { sb.append(" "); sb.append(av.getValue()); sb.append("\n"); } } return sb.toString(); } public class ArgInfo implements Comparable { private Arg arg; private String value; private SubVals subVals; private int argIndex; public ArgInfo(Arg arg, String value, SubVals subVals, int argIndex) { this.arg = arg; this.value = value; this.subVals = subVals; this.argIndex = argIndex; } public Arg arg() { return arg; } public String value() { return value; } public SubVals subVals() { return subVals; } public int argIndex() { return argIndex; } @Override public int compareTo(ArgInfo ai2) { return Integer.compare(this.argIndex(), ai2.argIndex()); } } public static enum Position { FIRST, BEFORE, AFTER } }