JAL-2069 update spike branch with latest
[jalview.git] / src / jalview / datamodel / features / FeatureAttributes.java
index d4e9fb0..3dc4f19 100644 (file)
@@ -5,8 +5,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.TreeMap;
 
 /**
  * A singleton class to hold the set of attributes known for each feature type
@@ -15,7 +14,91 @@ public class FeatureAttributes
 {
   private static FeatureAttributes instance = new FeatureAttributes();
 
-  private Map<String, Set<String>> attributes;
+  private Map<String, Map<String, AttributeData>> attributes;
+
+  private class AttributeData
+  {
+    /*
+     * description(s) for this attribute, if known
+     * (different feature source might have differing descriptions)
+     */
+    List<String> 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
@@ -46,7 +129,7 @@ public class FeatureAttributes
       return Collections.<String> emptyList();
     }
 
-    return new ArrayList<>(attributes.get(featureType));
+    return new ArrayList<>(attributes.get(featureType).keySet());
   }
 
   /**
@@ -58,7 +141,6 @@ public class FeatureAttributes
    */
   public boolean hasAttributes(String featureType)
   {
-
     if (attributes.containsKey(featureType))
     {
       if (!attributes.get(featureType).isEmpty())
@@ -70,24 +152,113 @@ public class FeatureAttributes
   }
 
   /**
-   * Records the given attribute name for the given feature type
+   * 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)
+  public void addAttribute(String featureType, String attName,
+          String description, String value)
   {
     if (featureType == null || attName == null)
     {
       return;
     }
 
-    if (!attributes.containsKey(featureType))
+    Map<String, AttributeData> atts = attributes.get(featureType);
+    if (atts == null)
+    {
+      atts = new TreeMap<String, AttributeData>(
+              String.CASE_INSENSITIVE_ORDER);
+      attributes.put(featureType, atts);
+    }
+    AttributeData attData = atts.get(attName);
+    if (attData == null)
     {
-      attributes.put(featureType, new TreeSet<String>(
-              String.CASE_INSENSITIVE_ORDER));
+      attData = new AttributeData();
+      atts.put(attName, attData);
     }
+    attData.addInstance(description, value);
+  }
 
-    attributes.get(featureType).add(attName);
+  /**
+   * 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<String, AttributeData> 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<String, AttributeData> 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<String, AttributeData> atts = attributes.get(featureType);
+    if (atts == null)
+    {
+      atts = new TreeMap<String, AttributeData>(
+              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);
   }
 }