From fa6001e965a3c35eaa93496d661a1f515dc0a426 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 26 Apr 2017 12:02:20 +0100 Subject: [PATCH] JAL-2490 findFeaturesAtRes with performant feature lookup --- src/jalview/api/FeaturesDisplayedI.java | 13 ++- src/jalview/datamodel/Sequence.java | 10 +- src/jalview/datamodel/SequenceI.java | 9 +- src/jalview/io/ClansFile.java | 30 ------ src/jalview/io/MatrixFile.java | 35 ------- .../renderer/seqfeatures/FeatureRenderer.java | 23 +---- .../seqfeatures/FeatureRendererModel.java | 81 +++++++++-------- .../viewmodel/seqfeatures/FeaturesDisplayed.java | 18 ++-- .../renderer/seqfeatures/FeatureRendererTest.java | 96 ++++++++++++++++++++ 9 files changed, 173 insertions(+), 142 deletions(-) delete mode 100644 src/jalview/io/ClansFile.java delete mode 100644 src/jalview/io/MatrixFile.java diff --git a/src/jalview/api/FeaturesDisplayedI.java b/src/jalview/api/FeaturesDisplayedI.java index 32b0565..e69785f 100644 --- a/src/jalview/api/FeaturesDisplayedI.java +++ b/src/jalview/api/FeaturesDisplayedI.java @@ -21,12 +21,15 @@ package jalview.api; import java.util.Collection; -import java.util.Iterator; +import java.util.Set; public interface FeaturesDisplayedI { - Iterator getVisibleFeatures(); + /** + * answers an unmodifiable view of the set of visible feature types + */ + Set 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 featureTypes); boolean isRegistered(String type); diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 49caa4c..c0549f2 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -1497,13 +1497,17 @@ public class Sequence extends ASequence implements SequenceI } } + /** + * {@inheritDoc} + */ @Override - public List findFeatures(String type, int from, int to) + public List 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); } } diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 605f682..1fbc76a 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -486,13 +486,14 @@ public interface SequenceI extends ASequenceI public List 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 findFeatures(String type, int from, int to); + List 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 index d0b1c72..0000000 --- a/src/jalview/io/ClansFile.java +++ /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 . - * 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 index 418eea2..0000000 --- a/src/jalview/io/MatrixFile.java +++ /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 . - * 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\tname\ttype\tvalue - * ROW\tRow i label (ID)/tPrinciple text/tprinciple description/t... - * COLUMN\t(similar, optional).. .. \t...(column-wise data for row - * i) - */ - -public class MatrixFile extends FileParse -{ - -} diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index e62b225..f4648c4 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -301,7 +301,8 @@ public class FeatureRenderer extends FeatureRendererModel continue; } - List overlaps = seq.findFeatures(type, startPos, endPos); + List 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 overlaps = seq.findFeatures(type, pos, pos); + List overlaps = seq.findFeatures(pos, pos, type); for (SequenceFeature sequenceFeature : overlaps) { if (!featureGroupNotShown(sequenceFeature)) diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 4ab050d..5d817c7 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -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 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 findFeaturesAtRes(SequenceI sequence, int res) { - ArrayList tmp = new ArrayList(); - SequenceFeature[] features = sequence.getSequenceFeatures(); - - if (features != null) + List result = new ArrayList(); + 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 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 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 features = getViewport().getFeaturesDisplayed() + Set 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(); + } + } diff --git a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java index 4c7e3c4..f44a2d1 100644 --- a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java +++ b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java @@ -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 featuresDisplayed = new HashSet(); + private Set featuresDisplayed = new HashSet(); - private HashSet featuresRegistered = new HashSet(); + private Set featuresRegistered = new HashSet(); public FeaturesDisplayed(FeaturesDisplayedI featuresDisplayed2) { - Iterator fdisp = featuresDisplayed2.getVisibleFeatures(); - String ftype; - while (fdisp.hasNext()) + Set 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 getVisibleFeatures() + public Set getVisibleFeatures() { - return featuresDisplayed.iterator(); + return Collections.unmodifiableSet(featuresDisplayed); } @Override diff --git a/test/jalview/renderer/seqfeatures/FeatureRendererTest.java b/test/jalview/renderer/seqfeatures/FeatureRendererTest.java index febd306..5bda316 100644 --- a/test/jalview/renderer/seqfeatures/FeatureRendererTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureRendererTest.java @@ -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 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)); + } } -- 1.7.10.2