JAL-2446 getGroups, getFeatures for positional/non-positional features
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 12 Apr 2017 09:51:55 +0000 (10:51 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 12 Apr 2017 09:51:55 +0000 (10:51 +0100)
src/jalview/datamodel/features/FeatureStore.java
src/jalview/datamodel/features/SequenceFeatures.java
test/jalview/datamodel/features/FeatureStoreTest.java
test/jalview/datamodel/features/SequenceFeaturesTest.java

index 6be33ef..9f61871 100644 (file)
@@ -57,10 +57,16 @@ public class FeatureStore
   NCList<SequenceFeature> nestedFeatures;
 
   /*
-   * Feature groups represented in the stored features 
+   * Feature groups represented in stored positional features 
    * (possibly including null)
    */
-  Set<String> featureGroups;
+  Set<String> positionalFeatureGroups;
+
+  /*
+   * Feature groups represented in stored non-positional features 
+   * (possibly including null)
+   */
+  Set<String> nonPositionalFeatureGroups;
 
   /**
    * Constructor
@@ -68,7 +74,7 @@ public class FeatureStore
   public FeatureStore()
   {
     nonNestedFeatures = new ArrayList<SequenceFeature>();
-    featureGroups = new HashSet<String>();
+    positionalFeatureGroups = new HashSet<String>();
 
     // we only construct nonPositionalFeatures, contactFeatures
     // or the NCList if we need to
@@ -84,7 +90,10 @@ public class FeatureStore
    */
   public boolean addFeature(SequenceFeature feature)
   {
-    featureGroups.add(feature.getFeatureGroup());
+    if (!feature.isNonPositional())
+    {
+      positionalFeatureGroups.add(feature.getFeatureGroup());
+    }
 
     boolean added = false;
 
@@ -118,7 +127,8 @@ public class FeatureStore
    * Adds the feature to the list of non-positional features (with lazy
    * instantiation of the list if it is null), and returns true. If the
    * non-positional features already include the new feature (by equality test),
-   * then it is not added, and this method returns false.
+   * then it is not added, and this method returns false. The feature group is
+   * added to the set of distinct feature groups for non-positional features.
    * 
    * @param feature
    */
@@ -127,12 +137,17 @@ public class FeatureStore
     if (nonPositionalFeatures == null)
     {
       nonPositionalFeatures = new ArrayList<SequenceFeature>();
+      nonPositionalFeatureGroups = new HashSet<String>();
     }
     if (nonPositionalFeatures.contains(feature))
     {
       return false;
     }
+
     nonPositionalFeatures.add(feature);
+
+    nonPositionalFeatureGroups.add(feature.getFeatureGroup());
+
     return true;
   }
 
@@ -506,12 +521,11 @@ public class FeatureStore
   }
 
   /**
-   * Answers a list of all features stored (including any non-positional
-   * features), in no guaranteed order
+   * Answers a list of all positional features stored, in no guaranteed order
    * 
    * @return
    */
-  public List<SequenceFeature> getFeatures()
+  public List<SequenceFeature> getPositionalFeatures()
   {
     /*
      * add non-nested features (may be all features for many cases)
@@ -528,14 +542,6 @@ public class FeatureStore
     }
 
     /*
-     * add any non-positional features
-     */
-    if (nonPositionalFeatures != null)
-    {
-      result.addAll(nonPositionalFeatures);
-    }
-
-    /*
      * add any nested features
      */
     if (nestedFeatures != null)
@@ -604,12 +610,15 @@ public class FeatureStore
       }
     }
 
+    boolean removedNonPositional = false;
+
     /*
      * if not found, try non-positional features
      */
     if (!removed && nonPositionalFeatures != null)
     {
-      removed = nonPositionalFeatures.remove(sf);
+      removedNonPositional = nonPositionalFeatures.remove(sf);
+      removed = removedNonPositional;
     }
 
     /*
@@ -622,30 +631,57 @@ public class FeatureStore
 
     if (removed)
     {
-      /*
-       * if this was the only feature for its feature group,
-       * remove the group from the stored set
-       */
-      String featureGroup = sf.getFeatureGroup();
-      if (!findFeatureGroup(featureGroup))
-      {
-        featureGroups.remove(featureGroup);
-      }
+      rebuildFeatureGroups(sf.getFeatureGroup(), removedNonPositional);
     }
 
     return removed;
   }
 
   /**
-   * Scans all features to check whether the given feature group is found, and
-   * returns true if found, else false
+   * Check whether the given feature group is still represented, in either
+   * positional or non-positional features, and if not, remove it from the set
+   * of feature groups
+   * 
+   * @param featureGroup
+   * @param nonPositional
+   */
+  protected void rebuildFeatureGroups(String featureGroup,
+          boolean nonPositional)
+  {
+    if (nonPositional && nonPositionalFeatures != null)
+    {
+      boolean found = false;
+      for (SequenceFeature sf : nonPositionalFeatures)
+      {
+        String group = sf.getFeatureGroup();
+        if (featureGroup == group
+                || (featureGroup != null && featureGroup.equals(group)))
+        {
+          found = true;
+          break;
+        }
+      }
+      if (!found)
+      {
+        nonPositionalFeatureGroups.remove(featureGroup);
+      }
+    }
+    else if (!findFeatureGroup(featureGroup))
+    {
+      positionalFeatureGroups.remove(featureGroup);
+    }
+  }
+
+  /**
+   * Scans all positional features to check whether the given feature group is
+   * found, and returns true if found, else false
    * 
    * @param featureGroup
    * @return
    */
   protected boolean findFeatureGroup(String featureGroup)
   {
-    for (SequenceFeature sf : getFeatures())
+    for (SequenceFeature sf : getPositionalFeatures())
     {
       String group = sf.getFeatureGroup();
       if (group == featureGroup
@@ -676,12 +712,23 @@ public class FeatureStore
 
   /**
    * Answers the set of distinct feature groups stored, possibly including null,
-   * as an unmodifiable view of the set.
+   * as an unmodifiable view of the set. The parameter determines whether the
+   * groups for positional or for non-positional features are returned.
    * 
+   * @param positionalFeatures
    * @return
    */
-  public Set<String> getFeatureGroups()
+  public Set<String> getFeatureGroups(boolean positionalFeatures)
   {
-    return Collections.unmodifiableSet(featureGroups);
+    if (positionalFeatures)
+    {
+      return Collections.unmodifiableSet(positionalFeatureGroups);
+    }
+    else
+    {
+      return nonPositionalFeatureGroups == null ? Collections
+              .<String> emptySet() : Collections
+              .unmodifiableSet(nonPositionalFeatureGroups);
+    }
   }
 }
index ba63f69..45d6ede 100644 (file)
@@ -76,22 +76,55 @@ public class SequenceFeatures
   }
 
   /**
-   * Answers a list of all features stored (including non-positional), in no
-   * particular guaranteed order
+   * Answers a list of all positional features stored, in no particular
+   * guaranteed order
    * 
    * @return
    */
-  public List<SequenceFeature> getFeatures()
+  public List<SequenceFeature> getPositionalFeatures()
   {
     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
     for (FeatureStore featureSet : featureStore.values())
     {
-      result.addAll(featureSet.getFeatures());
+      result.addAll(featureSet.getPositionalFeatures());
     }
     return result;
   }
 
   /**
+   * Answers a list of all features stored, in no particular guaranteed order
+   * 
+   * @return
+   */
+  public List<SequenceFeature> getAllFeatures()
+  {
+    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+
+    result.addAll(getPositionalFeatures());
+
+    result.addAll(getNonPositionalFeatures());
+
+    return result;
+  }
+
+  /**
+   * Answers a list of all features stored of the specified type, in no
+   * particular guaranteed order
+   * 
+   * @return
+   */
+  public List<SequenceFeature> getAllFeatures(String type)
+  {
+    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+
+    result.addAll(getPositionalFeatures(type));
+
+    result.addAll(getNonPositionalFeatures(type));
+
+    return result;
+  }
+
+  /**
    * Answers a list of all non-positional features stored, in no particular
    * guaranteed order
    * 
@@ -124,18 +157,18 @@ public class SequenceFeatures
   }
 
   /**
-   * Answers a list of all features of the given type (including
-   * non-positional), in no particular guaranteed order
+   * Answers a list of all positional features of the given type, in no
+   * particular guaranteed order
    * 
    * @return
    */
-  public List<SequenceFeature> getFeatures(String type)
+  public List<SequenceFeature> getPositionalFeatures(String type)
   {
     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
     FeatureStore featureSet = featureStore.get(type);
     if (featureSet != null)
     {
-      result.addAll(featureSet.getFeatures());
+      result.addAll(featureSet.getPositionalFeatures());
     }
     return result;
   }
@@ -213,39 +246,46 @@ public class SequenceFeatures
 
   /**
    * Returns a set of the distinct feature groups present in the collection. The
-   * set may include null.
+   * set may include null. The parameter determines whether the groups for
+   * positional or for non-positional features are returned.
    * 
+   * @param positionalFeatures
    * @return
    */
-  public Set<String> getFeatureGroups()
+  public Set<String> getFeatureGroups(boolean positionalFeatures)
   {
     Set<String> groups = new HashSet<String>();
     for (FeatureStore featureSet : featureStore.values())
     {
-      groups.addAll(featureSet.getFeatureGroups());
+      groups.addAll(featureSet.getFeatureGroups(positionalFeatures));
     }
     return groups;
   }
 
   /**
    * Answers the set of distinct feature types for which there is at least one
-   * feature with one of the given feature group(s)
+   * feature with one of the given feature group(s). The parameter determines
+   * whether the groups for positional or for non-positional features are
+   * returned.
    * 
+   * @param positionalFeatures
    * @param groups
    * @return
    */
-  public Set<String> getFeatureTypesForGroups(String... groups)
+  public Set<String> getFeatureTypesForGroups(boolean positionalFeatures,
+          String... groups)
   {
     Set<String> result = new HashSet<String>();
     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
     {
-      Set<String> featureGroups = featureType.getValue().getFeatureGroups();
+      Set<String> featureGroups = featureType.getValue().getFeatureGroups(
+              positionalFeatures);
       for (String group : groups)
       {
         if (featureGroups.contains(group))
         {
           /*
-           * yes this feature type includes a query group
+           * yes this feature type includes one of the query groups
            */
           result.add(featureType.getKey());
           break;
index 0945803..fa63637 100644 (file)
@@ -236,7 +236,7 @@ public class FeatureStoreTest
   }
 
   @Test(groups = "Functional")
-  public void testGetFeatures()
+  public void testGetPositionalFeatures()
   {
     FeatureStore store = new FeatureStore();
     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
@@ -267,15 +267,19 @@ public class FeatureStoreTest
             18, 45, Float.NaN, null);
     store.addFeature(sf7);
 
-    List<SequenceFeature> features = store.getFeatures();
-    assertEquals(features.size(), 7);
+    List<SequenceFeature> features = store.getPositionalFeatures();
+    assertEquals(features.size(), 6);
     assertTrue(features.contains(sf1));
     assertTrue(features.contains(sf2));
     assertTrue(features.contains(sf3));
     assertTrue(features.contains(sf4));
     assertTrue(features.contains(sf5));
-    assertTrue(features.contains(sf6));
+    assertFalse(features.contains(sf6));
     assertTrue(features.contains(sf7));
+
+    features = store.getNonPositionalFeatures();
+    assertEquals(features.size(), 1);
+    assertTrue(features.contains(sf6));
   }
 
   @Test(groups = "Functional")
@@ -283,21 +287,22 @@ public class FeatureStoreTest
   {
     FeatureStore store = new FeatureStore();
     SequenceFeature sf1 = addFeature(store, 10, 20);
-    assertTrue(store.getFeatures().contains(sf1));
+    assertTrue(store.getPositionalFeatures().contains(sf1));
 
     /*
      * simple deletion
      */
     assertTrue(store.delete(sf1));
-    assertTrue(store.getFeatures().isEmpty());
+    assertTrue(store.getPositionalFeatures().isEmpty());
 
     /*
      * non-positional feature deletion
      */
     SequenceFeature sf2 = addFeature(store, 0, 0);
-    assertTrue(store.getFeatures().contains(sf2));
+    assertFalse(store.getPositionalFeatures().contains(sf2));
+    assertTrue(store.getNonPositionalFeatures().contains(sf2));
     assertTrue(store.delete(sf2));
-    assertTrue(store.getFeatures().isEmpty());
+    assertTrue(store.getNonPositionalFeatures().isEmpty());
 
     /*
      * contact feature deletion
@@ -305,10 +310,10 @@ public class FeatureStoreTest
     SequenceFeature sf3 = new SequenceFeature("", "Disulphide Bond", 11,
             23, Float.NaN, null);
     store.addFeature(sf3);
-    assertEquals(store.getFeatures().size(), 1);
-    assertTrue(store.getFeatures().contains(sf3));
+    assertEquals(store.getPositionalFeatures().size(), 1);
+    assertTrue(store.getPositionalFeatures().contains(sf3));
     assertTrue(store.delete(sf3));
-    assertTrue(store.getFeatures().isEmpty());
+    assertTrue(store.getPositionalFeatures().isEmpty());
 
     /*
      * nested feature deletion
@@ -319,34 +324,34 @@ public class FeatureStoreTest
     SequenceFeature sf7 = addFeature(store, 25, 25); // sibling of sf6
     SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
     SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
-    assertEquals(store.getFeatures().size(), 6);
+    assertEquals(store.getPositionalFeatures().size(), 6);
 
     // delete a node with children - they take its place
     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
-    assertEquals(store.getFeatures().size(), 5);
-    assertFalse(store.getFeatures().contains(sf6));
+    assertEquals(store.getPositionalFeatures().size(), 5);
+    assertFalse(store.getPositionalFeatures().contains(sf6));
 
     // delete a node with no children
     assertTrue(store.delete(sf7));
-    assertEquals(store.getFeatures().size(), 4);
-    assertFalse(store.getFeatures().contains(sf7));
+    assertEquals(store.getPositionalFeatures().size(), 4);
+    assertFalse(store.getPositionalFeatures().contains(sf7));
 
     // delete root of NCList
     assertTrue(store.delete(sf5));
-    assertEquals(store.getFeatures().size(), 3);
-    assertFalse(store.getFeatures().contains(sf5));
+    assertEquals(store.getPositionalFeatures().size(), 3);
+    assertFalse(store.getPositionalFeatures().contains(sf5));
 
     // continue the killing fields
     assertTrue(store.delete(sf4));
-    assertEquals(store.getFeatures().size(), 2);
-    assertFalse(store.getFeatures().contains(sf4));
+    assertEquals(store.getPositionalFeatures().size(), 2);
+    assertFalse(store.getPositionalFeatures().contains(sf4));
 
     assertTrue(store.delete(sf9));
-    assertEquals(store.getFeatures().size(), 1);
-    assertFalse(store.getFeatures().contains(sf9));
+    assertEquals(store.getPositionalFeatures().size(), 1);
+    assertFalse(store.getPositionalFeatures().contains(sf9));
 
     assertTrue(store.delete(sf8));
-    assertTrue(store.getFeatures().isEmpty());
+    assertTrue(store.getPositionalFeatures().isEmpty());
   }
 
   @Test(groups = "Functional")
@@ -428,11 +433,11 @@ public class FeatureStoreTest
   public void testGetFeatureGroups()
   {
     FeatureStore fs = new FeatureStore();
-    assertTrue(fs.getFeatureGroups().isEmpty());
+    assertTrue(fs.getFeatureGroups(true).isEmpty());
 
     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
     fs.addFeature(sf1);
-    Set<String> groups = fs.getFeatureGroups();
+    Set<String> groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("group1"));
 
@@ -441,16 +446,16 @@ public class FeatureStoreTest
      */
     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
     fs.addFeature(sf2);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("group1"));
     fs.delete(sf2);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("group1"));
     fs.delete(sf1);
-    groups = fs.getFeatureGroups();
-    assertTrue(fs.getFeatureGroups().isEmpty());
+    groups = fs.getFeatureGroups(true);
+    assertTrue(fs.getFeatureGroups(true).isEmpty());
 
     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
     fs.addFeature(sf3);
@@ -458,22 +463,22 @@ public class FeatureStoreTest
     fs.addFeature(sf4);
     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
     fs.addFeature(sf5);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 3);
     assertTrue(groups.contains("group2"));
     assertTrue(groups.contains("Group2")); // case sensitive
     assertTrue(groups.contains(null)); // null allowed
 
     fs.delete(sf3);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 2);
     assertFalse(groups.contains("group2"));
     fs.delete(sf4);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertFalse(groups.contains("Group2"));
     fs.delete(sf5);
-    groups = fs.getFeatureGroups();
+    groups = fs.getFeatureGroups(true);
     assertTrue(groups.isEmpty());
   }
 }
index ea46d83..4b7609a 100644 (file)
@@ -14,7 +14,7 @@ import org.testng.annotations.Test;
 public class SequenceFeaturesTest
 {
   @Test(groups = "Functional")
-  public void testGetFeatures()
+  public void testGetPositionalFeatures()
   {
     SequenceFeatures store = new SequenceFeatures();
     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
@@ -53,16 +53,16 @@ public class SequenceFeaturesTest
     store.add(sf9);
 
     /*
-     * get all features
+     * get all positional features
      */
-    List<SequenceFeature> features = store.getFeatures();
-    assertEquals(features.size(), 9);
+    List<SequenceFeature> features = store.getPositionalFeatures();
+    assertEquals(features.size(), 8);
     assertTrue(features.contains(sf1));
     assertTrue(features.contains(sf2));
     assertTrue(features.contains(sf3));
     assertTrue(features.contains(sf4));
     assertTrue(features.contains(sf5));
-    assertTrue(features.contains(sf6));
+    assertFalse(features.contains(sf6)); // non-positional
     assertTrue(features.contains(sf7));
     assertTrue(features.contains(sf8));
     assertTrue(features.contains(sf9));
@@ -70,24 +70,24 @@ public class SequenceFeaturesTest
     /*
      * get features by type
      */
-    assertTrue(store.getFeatures(null).isEmpty());
-    assertTrue(store.getFeatures("Cath").isEmpty());
-    assertTrue(store.getFeatures("METAL").isEmpty());
+    assertTrue(store.getPositionalFeatures(null).isEmpty());
+    assertTrue(store.getPositionalFeatures("Cath").isEmpty());
+    assertTrue(store.getPositionalFeatures("METAL").isEmpty());
 
-    features = store.getFeatures("Metal");
-    assertEquals(features.size(), 6);
+    features = store.getPositionalFeatures("Metal");
+    assertEquals(features.size(), 5);
     assertTrue(features.contains(sf1));
     assertTrue(features.contains(sf2));
     assertTrue(features.contains(sf3));
     assertTrue(features.contains(sf4));
     assertTrue(features.contains(sf5));
-    assertTrue(features.contains(sf6));
+    assertFalse(features.contains(sf6));
 
-    features = store.getFeatures("Disulphide bond");
+    features = store.getPositionalFeatures("Disulphide bond");
     assertEquals(features.size(), 1);
     assertTrue(features.contains(sf7));
 
-    features = store.getFeatures("Pfam");
+    features = store.getPositionalFeatures("Pfam");
     assertEquals(features.size(), 2);
     assertTrue(features.contains(sf8));
     assertTrue(features.contains(sf9));
@@ -290,13 +290,13 @@ public class SequenceFeaturesTest
   {
     SequenceFeatures sf = new SequenceFeatures();
     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
-    assertTrue(sf.getFeatures().contains(sf1));
+    assertTrue(sf.getPositionalFeatures().contains(sf1));
 
     assertFalse(sf.delete(null));
     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 15, 0f, null);
     assertFalse(sf.delete(sf2)); // not added, can't delete it
     assertTrue(sf.delete(sf1));
-    assertTrue(sf.getFeatures().isEmpty());
+    assertTrue(sf.getPositionalFeatures().isEmpty());
   }
 
   @Test(groups = "Functional")
@@ -312,30 +312,75 @@ public class SequenceFeaturesTest
     assertFalse(sf.hasFeatures());
   }
 
+  /**
+   * Tests for the method that gets feature groups for positional or
+   * non-positional features
+   */
   @Test(groups = "Functional")
   public void testGetFeatureGroups()
   {
     SequenceFeatures sf = new SequenceFeatures();
-    assertTrue(sf.getFeatureGroups().isEmpty());
+    assertTrue(sf.getFeatureGroups(true).isEmpty());
+    assertTrue(sf.getFeatureGroups(false).isEmpty());
+
+    /*
+     * add a non-positional feature (begin/end = 0/0)
+     */
+    SequenceFeature sfx = new SequenceFeature("AType", "Desc", 0, 0, 0f,
+            "AGroup");
+    sf.add(sfx);
+    Set<String> groups = sf.getFeatureGroups(true); // for positional
+    assertTrue(groups.isEmpty());
+    groups = sf.getFeatureGroups(false); // for non-positional
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("AGroup"));
 
+    /*
+     * add, then delete, more non-positional features of different types
+     */
+    SequenceFeature sfy = new SequenceFeature("AnotherType", "Desc", 0, 0,
+            0f,
+            "AnotherGroup");
+    sf.add(sfy);
+    SequenceFeature sfz = new SequenceFeature("AThirdType", "Desc", 0, 0,
+            0f,
+            null);
+    sf.add(sfz);
+    groups = sf.getFeatureGroups(false);
+    assertEquals(groups.size(), 3);
+    assertTrue(groups.contains("AGroup"));
+    assertTrue(groups.contains("AnotherGroup"));
+    assertTrue(groups.contains(null)); // null is a possible group
+    sf.delete(sfz);
+    sf.delete(sfy);
+    groups = sf.getFeatureGroups(false);
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("AGroup"));
+
+    /*
+     * add positional features
+     */
     SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
             "PfamGroup");
     sf.add(sf1);
-    Set<String> groups = sf.getFeatureGroups();
+    groups = sf.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("PfamGroup"));
+    groups = sf.getFeatureGroups(false); // non-positional unchanged
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("AGroup"));
 
     SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
             null);
     sf.add(sf2);
-    groups = sf.getFeatureGroups();
+    groups = sf.getFeatureGroups(true);
     assertEquals(groups.size(), 2);
     assertTrue(groups.contains("PfamGroup"));
     assertTrue(groups.contains(null));
 
     sf.delete(sf1);
     sf.delete(sf2);
-    assertTrue(sf.getFeatureGroups().isEmpty());
+    assertTrue(sf.getFeatureGroups(true).isEmpty());
 
     SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
             "Ensembl");
@@ -343,7 +388,7 @@ public class SequenceFeaturesTest
     SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
             "Ensembl");
     sf.add(sf4);
-    groups = sf.getFeatureGroups();
+    groups = sf.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("Ensembl"));
 
@@ -352,16 +397,23 @@ public class SequenceFeaturesTest
      * but still have one in exon features
      */
     sf.delete(sf3);
-    groups = sf.getFeatureGroups();
+    groups = sf.getFeatureGroups(true);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("Ensembl"));
+
+    /*
+     * delete the last non-positional feature
+     */
+    sf.delete(sfx);
+    groups = sf.getFeatureGroups(false);
+    assertTrue(groups.isEmpty());
   }
 
   @Test(groups = "Functional")
   public void testGetFeatureTypesForGroups()
   {
     SequenceFeatures sf = new SequenceFeatures();
-    assertTrue(sf.getFeatureTypesForGroups((String) null).isEmpty());
+    assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
   
     /*
      * add feature with group = "Uniprot", type = "helix"
@@ -370,10 +422,10 @@ public class SequenceFeaturesTest
     SequenceFeature sf1 = new SequenceFeature("helix", "Desc", 10, 50, 0f,
             groupUniprot);
     sf.add(sf1);
-    Set<String> groups = sf.getFeatureTypesForGroups(groupUniprot);
+    Set<String> groups = sf.getFeatureTypesForGroups(true, groupUniprot);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("helix"));
-    assertTrue(sf.getFeatureTypesForGroups((String) null).isEmpty());
+    assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
   
     /*
      * add feature with group = "Uniprot", type = "strand"
@@ -381,7 +433,7 @@ public class SequenceFeaturesTest
     SequenceFeature sf2 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
             groupUniprot);
     sf.add(sf2);
-    groups = sf.getFeatureTypesForGroups(groupUniprot);
+    groups = sf.getFeatureTypesForGroups(true, groupUniprot);
     assertEquals(groups.size(), 2);
     assertTrue(groups.contains("helix"));
     assertTrue(groups.contains("strand"));
@@ -390,7 +442,7 @@ public class SequenceFeaturesTest
      * delete the "strand" Uniprot feature - still have "helix"
      */
     sf.delete(sf2);
-    groups = sf.getFeatureTypesForGroups(groupUniprot);
+    groups = sf.getFeatureTypesForGroups(true, groupUniprot);
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("helix"));
 
@@ -398,7 +450,7 @@ public class SequenceFeaturesTest
      * delete the "helix" Uniprot feature - none left
      */
     sf.delete(sf1);
-    assertTrue(sf.getFeatureTypesForGroups(groupUniprot).isEmpty());
+    assertTrue(sf.getFeatureTypesForGroups(true, groupUniprot).isEmpty());
 
     /*
      * add some null group features
@@ -409,7 +461,7 @@ public class SequenceFeaturesTest
     SequenceFeature sf4 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
             null);
     sf.add(sf4);
-    groups = sf.getFeatureTypesForGroups((String) null);
+    groups = sf.getFeatureTypesForGroups(true, (String) null);
     assertEquals(groups.size(), 2);
     assertTrue(groups.contains("strand"));
     assertTrue(groups.contains("turn"));
@@ -424,18 +476,19 @@ public class SequenceFeaturesTest
     SequenceFeature sf6 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
             "Scop");
     sf.add(sf6);
-    groups = sf.getFeatureTypesForGroups("Cath");
+    groups = sf.getFeatureTypesForGroups(true, "Cath");
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("strand"));
-    groups = sf.getFeatureTypesForGroups("Scop");
+    groups = sf.getFeatureTypesForGroups(true, "Scop");
     assertEquals(groups.size(), 1);
     assertTrue(groups.contains("turn"));
-    groups = sf.getFeatureTypesForGroups("Cath", "Scop");
+    groups = sf.getFeatureTypesForGroups(true, "Cath", "Scop");
     assertEquals(groups.size(), 2);
     assertTrue(groups.contains("turn"));
     assertTrue(groups.contains("strand"));
     // alternative vararg syntax
-    groups = sf.getFeatureTypesForGroups(new String[] { "Cath", "Scop" });
+    groups = sf.getFeatureTypesForGroups(true, new String[] { "Cath",
+        "Scop" });
     assertEquals(groups.size(), 2);
     assertTrue(groups.contains("turn"));
     assertTrue(groups.contains("strand"));