JAL-2446 added getFeatureGroups
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 11 Apr 2017 07:46:54 +0000 (08:46 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 11 Apr 2017 07:46:54 +0000 (08:46 +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 4e70dda..6be33ef 100644 (file)
@@ -5,7 +5,9 @@ import jalview.datamodel.SequenceFeature;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A data store for a set of sequence features that supports efficient lookup of
@@ -54,12 +56,20 @@ public class FeatureStore
    */
   NCList<SequenceFeature> nestedFeatures;
 
+  /*
+   * Feature groups represented in the stored features 
+   * (possibly including null)
+   */
+  Set<String> featureGroups;
+
   /**
    * Constructor
    */
   public FeatureStore()
   {
     nonNestedFeatures = new ArrayList<SequenceFeature>();
+    featureGroups = new HashSet<String>();
+
     // we only construct nonPositionalFeatures, contactFeatures
     // or the NCList if we need to
   }
@@ -74,6 +84,8 @@ public class FeatureStore
    */
   public boolean addFeature(SequenceFeature feature)
   {
+    featureGroups.add(feature.getFeatureGroup());
+
     boolean added = false;
 
     if (feature.isContactFeature())
@@ -572,7 +584,7 @@ public class FeatureStore
    * 
    * @param sf
    */
-  public boolean delete(SequenceFeature sf)
+  public synchronized boolean delete(SequenceFeature sf)
   {
     /*
      * try the non-nested positional features first
@@ -608,10 +620,44 @@ public class FeatureStore
       removed = nestedFeatures.delete(sf);
     }
 
+    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);
+      }
+    }
+
     return removed;
   }
 
   /**
+   * Scans all 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())
+    {
+      String group = sf.getFeatureGroup();
+      if (group == featureGroup
+              || (group != null && group.equals(featureGroup)))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * Answers true if this store has no features, else false
    * 
    * @return
@@ -627,4 +673,15 @@ public class FeatureStore
 
     return !hasFeatures;
   }
+
+  /**
+   * Answers the set of distinct feature groups stored, possibly including null,
+   * as an unmodifiable view of the set.
+   * 
+   * @return
+   */
+  public Set<String> getFeatureGroups()
+  {
+    return Collections.unmodifiableSet(featureGroups);
+  }
 }
index 227d8fd..a61eff2 100644 (file)
@@ -5,8 +5,10 @@ import jalview.datamodel.SequenceFeature;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A class that stores sequence features in a way that supports efficient
@@ -207,4 +209,20 @@ public class SequenceFeatures
     }
     return false;
   }
+
+  /**
+   * Returns a set of the distinct feature groups present in the collection. The
+   * set may include null.
+   * 
+   * @return
+   */
+  public Set<String> getFeatureGroups()
+  {
+    Set<String> groups = new HashSet<String>();
+    for (FeatureStore featureSet : featureStore.values())
+    {
+      groups.addAll(featureSet.getFeatureGroups());
+    }
+    return groups;
+  }
 }
index 171b7c3..0945803 100644 (file)
@@ -7,6 +7,7 @@ import static org.testng.Assert.assertTrue;
 import jalview.datamodel.SequenceFeature;
 
 import java.util.List;
+import java.util.Set;
 
 import org.testng.annotations.Test;
 
@@ -422,4 +423,57 @@ public class FeatureStoreTest
     assertTrue(fs.delete(sf3));
     assertTrue(fs.isEmpty()); // all gone
   }
+
+  @Test(groups = "Functional")
+  public void testGetFeatureGroups()
+  {
+    FeatureStore fs = new FeatureStore();
+    assertTrue(fs.getFeatureGroups().isEmpty());
+
+    SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
+    fs.addFeature(sf1);
+    Set<String> groups = fs.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("group1"));
+
+    /*
+     * add another feature of the same group, delete one, delete both
+     */
+    SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
+    fs.addFeature(sf2);
+    groups = fs.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("group1"));
+    fs.delete(sf2);
+    groups = fs.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("group1"));
+    fs.delete(sf1);
+    groups = fs.getFeatureGroups();
+    assertTrue(fs.getFeatureGroups().isEmpty());
+
+    SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
+    fs.addFeature(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
+    fs.addFeature(sf4);
+    SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
+    fs.addFeature(sf5);
+    groups = fs.getFeatureGroups();
+    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();
+    assertEquals(groups.size(), 2);
+    assertFalse(groups.contains("group2"));
+    fs.delete(sf4);
+    groups = fs.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertFalse(groups.contains("Group2"));
+    fs.delete(sf5);
+    groups = fs.getFeatureGroups();
+    assertTrue(groups.isEmpty());
+  }
 }
index 1ee21f3..d5da9fa 100644 (file)
@@ -7,6 +7,7 @@ import static org.testng.Assert.assertTrue;
 import jalview.datamodel.SequenceFeature;
 
 import java.util.List;
+import java.util.Set;
 
 import org.testng.annotations.Test;
 
@@ -310,4 +311,49 @@ public class SequenceFeaturesTest
     sf.delete(sf1);
     assertFalse(sf.hasFeatures());
   }
+
+  @Test(groups = "Functional")
+  public void testGetFeatureGroups()
+  {
+    SequenceFeatures sf = new SequenceFeatures();
+    assertTrue(sf.getFeatureGroups().isEmpty());
+
+    SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
+            "PfamGroup");
+    sf.add(sf1);
+    Set<String> groups = sf.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("PfamGroup"));
+
+    SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
+            null);
+    sf.add(sf2);
+    groups = sf.getFeatureGroups();
+    assertEquals(groups.size(), 2);
+    assertTrue(groups.contains("PfamGroup"));
+    assertTrue(groups.contains(null));
+
+    sf.delete(sf1);
+    sf.delete(sf2);
+    assertTrue(sf.getFeatureGroups().isEmpty());
+
+    SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
+            "Ensembl");
+    sf.add(sf3);
+    SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
+            "Ensembl");
+    sf.add(sf4);
+    groups = sf.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("Ensembl"));
+
+    /*
+     * delete last Ensembl group feature from CDS features
+     * but still have one in exon features
+     */
+    sf.delete(sf3);
+    groups = sf.getFeatureGroups();
+    assertEquals(groups.size(), 1);
+    assertTrue(groups.contains("Ensembl"));
+  }
 }