package jalview.datamodel.features; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * A singleton class to hold the set of attributes known for each feature type */ public class FeatureAttributes { private static FeatureAttributes instance = new FeatureAttributes(); private Map> attributes; private class AttributeData { /* * description(s) for this attribute, if known * (different feature source might have differing descriptions) */ List description; /* * minimum value (of any numeric values recorded) */ float min = 0f; /* * maximum value (of any numeric values recorded) */ float max = 0f; /* * flag is set true if any numeric value is detected for this attribute */ boolean hasValue = false; /** * Note one instance of this attribute, recording unique, non-null names, * and the min/max of any numerical values * * @param desc * @param value */ void addInstance(String desc, String value) { addDescription(desc); if (value != null) { try { float f = Float.valueOf(value); min = Float.min(min, f); max = Float.max(max, f); hasValue = true; } catch (NumberFormatException e) { // ok, wasn't a number, ignore for min-max purposes } } } /** * Answers the description of the attribute, if recorded and unique, or null if either no, or more than description is recorded * @return */ public String getDescription() { if (description != null && description.size() == 1) { return description.get(0); } return null; } /** * Adds the given description to the list of known descriptions (without * duplication) * * @param desc */ public void addDescription(String desc) { if (desc != null) { if (description == null) { description = new ArrayList<>(); } if (!description.contains(desc)) { description.add(desc); } } } } /** * Answers the singleton instance of this class * * @return */ public static FeatureAttributes getInstance() { return instance; } private FeatureAttributes() { attributes = new HashMap<>(); } /** * Answers the attributes known for the given feature type, in alphabetical * order (not case sensitive), or an empty set if no attributes are known * * @param featureType * @return */ public List getAttributes(String featureType) { if (!attributes.containsKey(featureType)) { return Collections. emptyList(); } return new ArrayList<>(attributes.get(featureType).keySet()); } /** * Answers true if at least one attribute is known for the given feature type, * else false * * @param featureType * @return */ public boolean hasAttributes(String featureType) { if (attributes.containsKey(featureType)) { if (!attributes.get(featureType).isEmpty()) { return true; } } return false; } /** * Records the given attribute name and description for the given feature * type, and updates the min-max for any numeric value * * @param featureType * @param attName * @param description * @param value */ public void addAttribute(String featureType, String attName, String description, String value) { if (featureType == null || attName == null) { return; } Map atts = attributes.get(featureType); if (atts == null) { atts = new TreeMap( String.CASE_INSENSITIVE_ORDER); attributes.put(featureType, atts); } AttributeData attData = atts.get(attName); if (attData == null) { attData = new AttributeData(); atts.put(attName, attData); } attData.addInstance(description, value); } /** * Answers the description of the given attribute for the given feature type, * if known and unique, else null * * @param featureType * @param attName * @return */ public String getDescription(String featureType, String attName) { String desc = null; Map atts = attributes.get(featureType); if (atts != null) { AttributeData attData = atts.get(attName); if (attData != null) { desc = attData.getDescription(); } } return desc; } /** * Answers the [min, max] value range of the given attribute for the given * feature type, if known, else null. Attributes which only have text values * would normally return null, however text values which happen to be numeric * could result in a 'min-max' range. * * @param featureType * @param attName * @return */ public float[] getMinMax(String featureType, String attName) { Map atts = attributes.get(featureType); if (atts != null) { AttributeData attData = atts.get(attName); if (attData != null && attData.hasValue) { return new float[] { attData.min, attData.max }; } } return null; } /** * Records the given attribute description for the given feature type * * @param featureType * @param attName * @param description */ public void addDescription(String featureType, String attName, String description) { if (featureType == null || attName == null) { return; } Map atts = attributes.get(featureType); if (atts == null) { atts = new TreeMap( String.CASE_INSENSITIVE_ORDER); attributes.put(featureType, atts); } AttributeData attData = atts.get(attName); if (attData == null) { attData = new AttributeData(); atts.put(attName, attData); } attData.addDescription(description); } }