JAL-2490 findFeaturesAtRes with performant feature lookup
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 26 Apr 2017 11:02:20 +0000 (12:02 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 26 Apr 2017 11:02:20 +0000 (12:02 +0100)
src/jalview/api/FeaturesDisplayedI.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceI.java
src/jalview/io/ClansFile.java [deleted file]
src/jalview/io/MatrixFile.java [deleted file]
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
test/jalview/renderer/seqfeatures/FeatureRendererTest.java

index 32b0565..e69785f 100644 (file)
 package jalview.api;
 
 import java.util.Collection;
-import java.util.Iterator;
+import java.util.Set;
 
 public interface FeaturesDisplayedI
 {
 
-  Iterator<String> getVisibleFeatures();
+  /**
+   * answers an unmodifiable view of the set of visible feature types
+   */
+  Set<String> getVisibleFeatures();
 
   boolean isVisible(String featureType);
 
@@ -36,6 +39,12 @@ public interface FeaturesDisplayedI
 
   void setVisible(String featureType);
 
+  /**
+   * Sets all the specified feature types to visible. Visibility of other
+   * feature types is not changed.
+   * 
+   * @param featureTypes
+   */
   void setAllVisible(Collection<String> featureTypes);
 
   boolean isRegistered(String type);
index 49caa4c..c0549f2 100755 (executable)
@@ -1497,13 +1497,17 @@ public class Sequence extends ASequence implements SequenceI
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
-  public List<SequenceFeature> findFeatures(String type, int from, int to)
+  public List<SequenceFeature> findFeatures(int from, int to,
+          String... types)
   {
     if (datasetSequence != null)
     {
-      return datasetSequence.findFeatures(type, from, to);
+      return datasetSequence.findFeatures(from, to, types);
     }
-    return sequenceFeatureStore.findFeatures(from, to, type);
+    return sequenceFeatureStore.findFeatures(from, to, types);
   }
 }
index 605f682..1fbc76a 100755 (executable)
@@ -486,13 +486,14 @@ public interface SequenceI extends ASequenceI
   public List<DBRefEntry> getPrimaryDBRefs();
 
   /**
-   * Returns a (possibly empty) list of sequence features of the given type that
-   * overlap the range from-to (inclusive)
+   * Returns a (possibly empty) list of sequence features that overlap the range
+   * from-to (inclusive), optionally restricted to one or more specified feature
+   * types
    * 
-   * @param type
    * @param from
    * @param to
+   * @param types
    * @return
    */
-  List<SequenceFeature> findFeatures(String type, int from, int to);
+  List<SequenceFeature> findFeatures(int from, int to, String... types);
 }
diff --git a/src/jalview/io/ClansFile.java b/src/jalview/io/ClansFile.java
deleted file mode 100644 (file)
index d0b1c72..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.io;
-
-/**
- * Read or write a CLANS style score matrix file.
- */
-
-public class ClansFile extends FileParse
-{
-
-}
diff --git a/src/jalview/io/MatrixFile.java b/src/jalview/io/MatrixFile.java
deleted file mode 100644 (file)
index 418eea2..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.io;
-
-/**
- * IO for asymmetric matrix with arbitrary dimension with labels, as displayed
- * by PCA viewer. Form is: tab separated entity defs header line TITLE\ttitle
- * DESC\tdesc PROPERTY\t<id or empty for whole matrix>\tname\ttype\tvalue
- * ROW\tRow i label (ID)/tPrinciple text/tprinciple description/t...
- * COLUMN\t(similar, optional).. .. <float>\t<float>...(column-wise data for row
- * i)
- */
-
-public class MatrixFile extends FileParse
-{
-
-}
index e62b225..f4648c4 100644 (file)
@@ -301,7 +301,8 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
-      List<SequenceFeature> overlaps = seq.findFeatures(type, startPos, endPos);
+      List<SequenceFeature> overlaps = seq.findFeatures(startPos, endPos,
+              type);
       for (SequenceFeature sequenceFeature : overlaps)
       {
         /*
@@ -379,24 +380,6 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
-   * Answers true if the feature belongs to a feature group which is not
-   * currently displayed, else false
-   * 
-   * @param sequenceFeature
-   * @return
-   */
-  protected boolean featureGroupNotShown(
-          final SequenceFeature sequenceFeature)
-  {
-    return featureGroups != null
-            && sequenceFeature.featureGroup != null
-            && sequenceFeature.featureGroup.length() != 0
-            && featureGroups.containsKey(sequenceFeature.featureGroup)
-            && !featureGroups.get(sequenceFeature.featureGroup)
-                    .booleanValue();
-  }
-
-  /**
    * Called when alignment in associated view has new/modified features to
    * discover and display.
    * 
@@ -436,7 +419,7 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
-      List<SequenceFeature> overlaps = seq.findFeatures(type, pos, pos);
+      List<SequenceFeature> overlaps = seq.findFeatures(pos, pos, type);
       for (SequenceFeature sequenceFeature : overlaps)
       {
         if (!featureGroupNotShown(sequenceFeature))
index 4ab050d..5d817c7 100644 (file)
@@ -39,7 +39,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -117,11 +116,10 @@ public abstract class FeatureRendererModel implements
           synchronized (fd)
           {
             fd.clear();
-            java.util.Iterator<String> fdisp = _fr.getFeaturesDisplayed()
-                    .getVisibleFeatures();
-            while (fdisp.hasNext())
+            for (String type : _fr.getFeaturesDisplayed()
+                    .getVisibleFeatures())
             {
-              fd.setVisible(fdisp.next());
+              fd.setVisible(type);
             }
           }
         }
@@ -267,41 +265,32 @@ public abstract class FeatureRendererModel implements
   @Override
   public List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res)
   {
-    ArrayList<SequenceFeature> tmp = new ArrayList<SequenceFeature>();
-    SequenceFeature[] features = sequence.getSequenceFeatures();
-
-    if (features != null)
+    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+    if (!av.areFeaturesDisplayed())
     {
-      for (int i = 0; i < features.length; i++)
-      {
-        if (!av.areFeaturesDisplayed()
-                || !av.getFeaturesDisplayed().isVisible(
-                        features[i].getType()))
-        {
-          continue;
-        }
+      return result;
+    }
 
-        if (features[i].featureGroup != null
-                && featureGroups != null
-                && featureGroups.containsKey(features[i].featureGroup)
-                && !featureGroups.get(features[i].featureGroup)
-                        .booleanValue())
-        {
-          continue;
-        }
+    Set<String> visibleFeatures = getFeaturesDisplayed()
+            .getVisibleFeatures();
+    String[] visibleTypes = visibleFeatures
+            .toArray(new String[visibleFeatures.size()]);
 
-        // check if start/end are at res, and if not a contact feature, that res
-        // lies between start and end
-        if ((features[i].getBegin() == res || features[i].getEnd() == res)
-                || (!features[i].isContactFeature()
-                        && (features[i].getBegin() < res) && (features[i]
-                        .getEnd() >= res)))
-        {
-          tmp.add(features[i]);
-        }
+    /*
+     * include features at the position provided their feature type is 
+     * displayed, and feature group is null or marked for display
+     */
+    List<SequenceFeature> features = sequence.getFeatures().findFeatures(
+            res, res, visibleTypes);
+
+    for (SequenceFeature sf : features)
+    {
+      if (!featureGroupNotShown(sf))
+      {
+        result.add(sf);
       }
     }
-    return tmp;
+    return result;
   }
 
   /**
@@ -905,11 +894,10 @@ public abstract class FeatureRendererModel implements
     {
       return fcols;
     }
-    Iterator<String> features = getViewport().getFeaturesDisplayed()
+    Set<String> features = getViewport().getFeaturesDisplayed()
             .getVisibleFeatures();
-    while (features.hasNext())
+    for (String feature : features)
     {
-      String feature = features.next();
       fcols.put(feature, getFeatureStyle(feature));
     }
     return fcols;
@@ -972,4 +960,21 @@ public abstract class FeatureRendererModel implements
     return _gps;
   }
 
+  /**
+   * Answers true if the feature belongs to a feature group which is not
+   * currently displayed, else false
+   * 
+   * @param sequenceFeature
+   * @return
+   */
+  protected boolean featureGroupNotShown(final SequenceFeature sequenceFeature)
+  {
+    return featureGroups != null
+            && sequenceFeature.featureGroup != null
+            && sequenceFeature.featureGroup.length() != 0
+            && featureGroups.containsKey(sequenceFeature.featureGroup)
+            && !featureGroups.get(sequenceFeature.featureGroup)
+                    .booleanValue();
+  }
+
 }
index 4c7e3c4..f44a2d1 100644 (file)
@@ -23,22 +23,21 @@ package jalview.viewmodel.seqfeatures;
 import jalview.api.FeaturesDisplayedI;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Set;
 
 public class FeaturesDisplayed implements FeaturesDisplayedI
 {
-  private HashSet<String> featuresDisplayed = new HashSet<String>();
+  private Set<String> featuresDisplayed = new HashSet<String>();
 
-  private HashSet<String> featuresRegistered = new HashSet<String>();
+  private Set<String> featuresRegistered = new HashSet<String>();
 
   public FeaturesDisplayed(FeaturesDisplayedI featuresDisplayed2)
   {
-    Iterator<String> fdisp = featuresDisplayed2.getVisibleFeatures();
-    String ftype;
-    while (fdisp.hasNext())
+    Set<String> fdisp = featuresDisplayed2.getVisibleFeatures();
+    for (String ftype : fdisp)
     {
-      ftype = fdisp.next();
       featuresDisplayed.add(ftype);
       featuresRegistered.add(ftype);
     }
@@ -46,13 +45,12 @@ public class FeaturesDisplayed implements FeaturesDisplayedI
 
   public FeaturesDisplayed()
   {
-    // TODO Auto-generated constructor stub
   }
 
   @Override
-  public Iterator<String> getVisibleFeatures()
+  public Set<String> getVisibleFeatures()
   {
-    return featuresDisplayed.iterator();
+    return Collections.unmodifiableSet(featuresDisplayed);
   }
 
   @Override
index febd306..5bda316 100644 (file)
@@ -130,4 +130,100 @@ public class FeatureRendererTest
     assertEquals(fr.getDisplayedFeatureTypes(),
             Arrays.asList("Rfam", "Metal"));
   }
+
+  @Test(groups = "Functional")
+  public void testFindFeaturesAtRes()
+  {
+    String seqData = ">s1\nabcdefghijklmnopqrstuvwxyz\n";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            DataSourceType.PASTE);
+    AlignViewportI av = af.getViewport();
+    FeatureRenderer fr = new FeatureRenderer(av);
+    SequenceI seq = av.getAlignment().getSequenceAt(0);
+
+    /*
+     * with no features
+     */
+    List<SequenceFeature> features = fr.findFeaturesAtRes(seq, 3);
+    assertTrue(features.isEmpty());
+
+    /*
+     * add features
+     */
+    SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
+            "Group"); // non-positional
+    seq.addSequenceFeature(sf1);
+    SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 5, 15, 1f,
+            "Group1");
+    seq.addSequenceFeature(sf2);
+    SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 5, 15, 1f,
+            "Group2");
+    seq.addSequenceFeature(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 5, 15, 1f,
+            null); // null group is always treated as visible
+    seq.addSequenceFeature(sf4);
+
+    /*
+     * add contact features
+     */
+    SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 4,
+            12, 1f, "Group1");
+    seq.addSequenceFeature(sf5);
+    SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 4,
+            12, 1f, "Group2");
+    seq.addSequenceFeature(sf6);
+    SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 4,
+            12, 1f, null);
+    seq.addSequenceFeature(sf7);
+
+    /*
+     * let feature renderer discover features (and make visible)
+     */
+    fr.findAllFeatures(true);
+    features = fr.findFeaturesAtRes(seq, 12); // all positional
+    assertEquals(features.size(), 6);
+    assertTrue(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf6));
+    assertTrue(features.contains(sf7));
+
+    /*
+     * at a non-contact position
+     */
+    features = fr.findFeaturesAtRes(seq, 11);
+    assertEquals(features.size(), 3);
+    assertTrue(features.contains(sf2));
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+
+    /*
+     * make "Type2" not displayed
+     */
+    Object[][] data = new Object[4][];
+    FeatureColourI colour = new FeatureColour(Color.RED);
+    data[0] = new Object[] { "Type1", colour, true };
+    data[1] = new Object[] { "Type2", colour, false };
+    data[2] = new Object[] { "Type3", colour, true };
+    data[3] = new Object[] { "Disulphide Bond", colour, true };
+    fr.setFeaturePriority(data);
+    features = fr.findFeaturesAtRes(seq, 12);
+    assertEquals(features.size(), 5); // no sf2
+    assertTrue(features.contains(sf3));
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf6));
+    assertTrue(features.contains(sf7));
+
+    /*
+     * make "Group2" not displayed
+     */
+    fr.setGroupVisibility("Group2", false);
+    features = fr.findFeaturesAtRes(seq, 12);
+    assertEquals(features.size(), 3); // no sf2, sf3, sf6
+    assertTrue(features.contains(sf4));
+    assertTrue(features.contains(sf5));
+    assertTrue(features.contains(sf7));
+  }
 }