JAL-2446 various get and find methods added with test coverage
[jalview.git] / src / jalview / datamodel / features / FeatureStore.java
index bd94c8a..f7757be 100644 (file)
@@ -9,7 +9,8 @@ import java.util.List;
 
 /**
  * A data store for a set of sequence features that supports efficient lookup of
- * features overlapping a given range.
+ * features overlapping a given range. Intended for (but not limited to) storage
+ * of features for one sequence and feature type.
  * 
  * @author gmcarstairs
  *
@@ -21,6 +22,11 @@ public class FeatureStore
   Comparator<ContiguousI> endOrdering = new RangeComparator(false);
 
   /*
+   * Non-positional features have no (zero) start/end position.
+   */
+  List<SequenceFeature> nonPositionalFeatures;
+
+  /*
    * An ordered list of features, with the promise that no feature in the list 
    * properly contains any other. This constraint allows bounded linear search
    * of the list for features overlapping a region.
@@ -67,6 +73,10 @@ public class FeatureStore
     {
       addContactFeature(feature);
     }
+    else if (feature.isNonPositional())
+    {
+      addNonPositionalFeature(feature);
+    }
     else
     {
       boolean added = addNonNestedFeature(feature);
@@ -81,6 +91,21 @@ public class FeatureStore
   }
 
   /**
+   * Adds the feature to the list of non-positional features (with lazy
+   * instantiation of the list if it is null)
+   * 
+   * @param feature
+   */
+  protected void addNonPositionalFeature(SequenceFeature feature)
+  {
+    if (nonPositionalFeatures == null)
+    {
+      nonPositionalFeatures = new ArrayList<SequenceFeature>();
+    }
+    nonPositionalFeatures.add(feature);
+  }
+
+  /**
    * Adds one feature to the NCList that can manage nested features (creating
    * the NCList if necessary)
    */
@@ -370,10 +395,6 @@ public class FeatureStore
       }
       int start = sf.getBegin();
       int end = sf.getEnd();
-      if (sf.isContactFeature())
-      {
-        end = start;
-      }
       if (start <= to && end >= from)
       {
         result.add(sf);
@@ -442,4 +463,75 @@ public class FeatureStore
       }
     }
   }
+
+  /**
+   * Answers a list of all features stored (including any non-positional
+   * features), in no guaranteed order
+   * 
+   * @return
+   */
+  public List<SequenceFeature> getFeatures()
+  {
+    /*
+     * add non-nested features (may be all features for many cases)
+     */
+    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+    result.addAll(nonNestedFeatures);
+
+    /*
+     * add any contact features - from the list by start position
+     */
+    if (contactFeatureStarts != null)
+    {
+      result.addAll(contactFeatureStarts);
+    }
+
+    /*
+     * add any non-positional features
+     */
+    if (nonPositionalFeatures != null)
+    {
+      result.addAll(nonPositionalFeatures);
+    }
+
+    /*
+     * add any nested features
+     */
+    if (nestedFeatures != null)
+    {
+      result.addAll(nestedFeatures.getEntries());
+    }
+
+    return result;
+  }
+
+  /**
+   * Answers a list of all contact features. If there are none, returns an
+   * immutable empty list.
+   * 
+   * @return
+   */
+  public List<SequenceFeature> getContactFeatures()
+  {
+    if (contactFeatureStarts == null)
+    {
+      return Collections.emptyList();
+    }
+    return new ArrayList<SequenceFeature>(contactFeatureStarts);
+  }
+
+  /**
+   * Answers a list of all non-positional features. If there are none, returns
+   * an immutable empty list.
+   * 
+   * @return
+   */
+  public List<SequenceFeature> getNonPositionalFeatures()
+  {
+    if (nonPositionalFeatures == null)
+    {
+      return Collections.emptyList();
+    }
+    return new ArrayList<SequenceFeature>(nonPositionalFeatures);
+  }
 }