From 30355a6f53c842429f4ca2e31f60a7c5de167f1a Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 5 Apr 2017 09:14:28 +0100 Subject: [PATCH] JAL-2446 various get and find methods added with test coverage --- src/jalview/datamodel/features/FeatureStore.java | 102 ++++++- src/jalview/datamodel/features/NCList.java | 32 ++- src/jalview/datamodel/features/NCNode.java | 21 +- .../datamodel/features/SequenceFeatures.java | 110 ++++++++ .../datamodel/features/FeatureStoreTest.java | 43 +++ test/jalview/datamodel/features/NCListTest.java | 64 +++-- test/jalview/datamodel/features/NCNodeTest.java | 78 ++++++ test/jalview/datamodel/features/Range.java | 32 +++ .../datamodel/features/SequenceFeaturesTest.java | 286 ++++++++++++++++++++ 9 files changed, 730 insertions(+), 38 deletions(-) create mode 100644 test/jalview/datamodel/features/NCNodeTest.java create mode 100644 test/jalview/datamodel/features/Range.java create mode 100644 test/jalview/datamodel/features/SequenceFeaturesTest.java diff --git a/src/jalview/datamodel/features/FeatureStore.java b/src/jalview/datamodel/features/FeatureStore.java index bd94c8a..f7757be 100644 --- a/src/jalview/datamodel/features/FeatureStore.java +++ b/src/jalview/datamodel/features/FeatureStore.java @@ -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 endOrdering = new RangeComparator(false); /* + * Non-positional features have no (zero) start/end position. + */ + List 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(); + } + 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 getFeatures() + { + /* + * add non-nested features (may be all features for many cases) + */ + List result = new ArrayList(); + 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 getContactFeatures() + { + if (contactFeatureStarts == null) + { + return Collections.emptyList(); + } + return new ArrayList(contactFeatureStarts); + } + + /** + * Answers a list of all non-positional features. If there are none, returns + * an immutable empty list. + * + * @return + */ + public List getNonPositionalFeatures() + { + if (nonPositionalFeatures == null) + { + return Collections.emptyList(); + } + return new ArrayList(nonPositionalFeatures); + } } diff --git a/src/jalview/datamodel/features/NCList.java b/src/jalview/datamodel/features/NCList.java index 40b062a..6a9f750 100644 --- a/src/jalview/datamodel/features/NCList.java +++ b/src/jalview/datamodel/features/NCList.java @@ -9,7 +9,10 @@ import java.util.List; * An adapted implementation of NCList as described in the paper * *
- * todo
+ * Nested Containment List (NCList): a new algorithm for accelerating
+ * interval query of genome alignment and interval databases
+ * - Alexander V. Alekseyenko, Christopher J. Lee
+ * https://doi.org/10.1093/bioinformatics/btl647
  * 
*/ public class NCList @@ -312,7 +315,7 @@ public class NCList */ break; } - candidate.addOverlaps(from, to, result); + candidate.findOverlaps(from, to, result); } } @@ -461,4 +464,29 @@ public class NCList { return size; } + + /** + * Returns a list of all entries stored + * + * @return + */ + public List getEntries() + { + List result = new ArrayList(); + getEntries(result); + return result; + } + + /** + * Adds all contained entries to the given list + * + * @param result + */ + void getEntries(List result) + { + for (NCNode subrange : subranges) + { + subrange.getEntries(result); + } + } } diff --git a/src/jalview/datamodel/features/NCNode.java b/src/jalview/datamodel/features/NCNode.java index ab10f67..6af913a 100644 --- a/src/jalview/datamodel/features/NCNode.java +++ b/src/jalview/datamodel/features/NCNode.java @@ -18,6 +18,9 @@ class NCNode private V region; + /* + * null, or an object holding contained subregions of this nodes region + */ private NCList subregions; /** @@ -46,7 +49,7 @@ class NCNode { region = entry; subregions = newNCList; - // size = 1 + newNCList.size(); + size = 1 + newNCList.getSize(); } /** @@ -112,7 +115,7 @@ class NCNode * @param to * @param result */ - void addOverlaps(long from, long to, List result) + void findOverlaps(long from, long to, List result) { if (region.getBegin() <= to && region.getEnd() >= from) { @@ -161,4 +164,18 @@ class NCNode } return subregions.isValid(getStart(), getEnd()); } + + /** + * Adds all contained entries to the given list + * + * @param entries + */ + void getEntries(List entries) + { + entries.add(region); + if (subregions != null) + { + subregions.getEntries(entries); + } + } } diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index d177566..c947407 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -2,16 +2,26 @@ package jalview.datamodel.features; import jalview.datamodel.SequenceFeature; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * A class that stores sequence features in a way that supports efficient + * querying by type and location (overlap). Intended for (but not limited to) + * storage of features for one sequence. + * + * @author gmcarstairs + * + */ public class SequenceFeatures { /* * map from feature type to structured store of features for that type + * null types are permitted (but not a good idea!) */ private Map featureStore; @@ -31,6 +41,7 @@ public class SequenceFeatures public void add(SequenceFeature sf) { String type = sf.getType(); + if (featureStore.get(type) == null) { featureStore.put(type, new FeatureStore()); @@ -57,4 +68,103 @@ public class SequenceFeatures } return features.findOverlappingFeatures(from, to); } + + /** + * Answers a list of all features stored (including non-positional), in no + * particular guaranteed order + * + * @return + */ + public List getFeatures() + { + List result = new ArrayList(); + for (FeatureStore featureSet : featureStore.values()) + { + result.addAll(featureSet.getFeatures()); + } + return result; + } + + /** + * Answers a list of all non-positional features stored, in no particular + * guaranteed order + * + * @return + */ + public List getNonPositionalFeatures() + { + List result = new ArrayList(); + for (FeatureStore featureSet : featureStore.values()) + { + result.addAll(featureSet.getNonPositionalFeatures()); + } + return result; + } + + /** + * Answers a list of all contact features stored, in no particular guaranteed + * order + * + * @return + */ + public List getContactFeatures() + { + List result = new ArrayList(); + for (FeatureStore featureSet : featureStore.values()) + { + result.addAll(featureSet.getContactFeatures()); + } + return result; + } + + /** + * Answers a list of all features of the given type (including + * non-positional), in no particular guaranteed order + * + * @return + */ + public List getFeatures(String type) + { + List result = new ArrayList(); + FeatureStore featureSet = featureStore.get(type); + if (featureSet != null) + { + result.addAll(featureSet.getFeatures()); + } + return result; + } + + /** + * Answers a list of all contact features of the given type, in no particular + * guaranteed order + * + * @return + */ + public List getContactFeatures(String type) + { + List result = new ArrayList(); + FeatureStore featureSet = featureStore.get(type); + if (featureSet != null) + { + result.addAll(featureSet.getContactFeatures()); + } + return result; + } + + /** + * Answers a list of all non-positional features of the given type, in no + * particular guaranteed order + * + * @return + */ + public List getNonPositionalFeatures(String type) + { + List result = new ArrayList(); + FeatureStore featureSet = featureStore.get(type); + if (featureSet != null) + { + result.addAll(featureSet.getNonPositionalFeatures()); + } + return result; + } } diff --git a/test/jalview/datamodel/features/FeatureStoreTest.java b/test/jalview/datamodel/features/FeatureStoreTest.java index e355aaf..7147c51 100644 --- a/test/jalview/datamodel/features/FeatureStoreTest.java +++ b/test/jalview/datamodel/features/FeatureStoreTest.java @@ -229,4 +229,47 @@ public class FeatureStoreTest null); assertFalse(fs.addNonNestedFeature(sf10)); } + + @Test(groups = "Functional") + public void testGetFeatures() + { + FeatureStore store = new FeatureStore(); + SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.addFeature(sf1); + // same range + SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.addFeature(sf2); + // discontiguous range + SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40, + Float.NaN, null); + store.addFeature(sf3); + // overlapping range + SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35, + Float.NaN, null); + store.addFeature(sf4); + // enclosing range + SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50, + Float.NaN, null); + store.addFeature(sf5); + // non-positional feature + SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0, + Float.NaN, null); + store.addFeature(sf6); + // contact feature + SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc", + 18, 45, Float.NaN, null); + store.addFeature(sf7); + + List features = store.getFeatures(); + assertEquals(features.size(), 7); + assertTrue(features.contains(sf1)); + assertTrue(features.contains(sf2)); + assertTrue(features.contains(sf3)); + assertTrue(features.contains(sf4)); + assertTrue(features.contains(sf5)); + assertTrue(features.contains(sf6)); + assertTrue(features.contains(sf7)); + } } diff --git a/test/jalview/datamodel/features/NCListTest.java b/test/jalview/datamodel/features/NCListTest.java index 367e424..cb3a133 100644 --- a/test/jalview/datamodel/features/NCListTest.java +++ b/test/jalview/datamodel/features/NCListTest.java @@ -12,35 +12,6 @@ import org.testng.annotations.Test; public class NCListTest { - class Range implements ContiguousI - { - int start; - - int end; - - @Override - public int getBegin() - { - return start; - } - - @Override - public int getEnd() - { - return end; - } - - Range(int i, int j) - { - start = i; - end = j; - } - - @Override - public String toString() { - return String.valueOf(start) + "-" + String.valueOf(end); - } - } /** * A basic sanity test of the constructor @@ -202,4 +173,39 @@ public class NCListTest System.out.println(ncl.prettyPrint()); } } + + @Test(groups = "Functional") + public void testGetEntries() + { + List ranges = new ArrayList(); + Range r1 = new Range(20, 20); + Range r2 = new Range(10, 20); + Range r3 = new Range(15, 30); + Range r4 = new Range(10, 30); + Range r5 = new Range(11, 19); + Range r6 = new Range(10, 20); + ranges.add(r1); + ranges.add(r2); + ranges.add(r3); + ranges.add(r4); + ranges.add(r5); + ranges.add(r6); + + NCList ncl = new NCList(ranges); + Range r7 = new Range(1, 100); + ncl.add(r7); + + List contents = ncl.getEntries(); + assertEquals(contents.size(), 7); + assertTrue(contents.contains(r1)); + assertTrue(contents.contains(r2)); + assertTrue(contents.contains(r3)); + assertTrue(contents.contains(r4)); + assertTrue(contents.contains(r5)); + assertTrue(contents.contains(r6)); + assertTrue(contents.contains(r7)); + + ncl = new NCList(); + assertTrue(ncl.getEntries().isEmpty()); + } } diff --git a/test/jalview/datamodel/features/NCNodeTest.java b/test/jalview/datamodel/features/NCNodeTest.java new file mode 100644 index 0000000..da0aa4e --- /dev/null +++ b/test/jalview/datamodel/features/NCNodeTest.java @@ -0,0 +1,78 @@ +package jalview.datamodel.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.testng.annotations.Test; + +public class NCNodeTest +{ + @Test(groups = "Functional") + public void testAdd() + { + Range r1 = new Range(10, 20); + NCNode node = new NCNode(r1); + assertEquals(node.getStart(), 10); + Range r2 = new Range(10, 15); + node.add(r2); + + List contents = new ArrayList(); + node.getEntries(contents); + assertEquals(contents.size(), 2); + assertTrue(contents.contains(r1)); + assertTrue(contents.contains(r2)); + } + + @Test( + groups = "Functional", + expectedExceptions = { IllegalArgumentException.class }) + public void testAdd_invalidRangeStart() + { + Range r1 = new Range(10, 20); + NCNode node = new NCNode(r1); + assertEquals(node.getStart(), 10); + Range r2 = new Range(9, 15); + node.add(r2); + } + + @Test( + groups = "Functional", + expectedExceptions = { IllegalArgumentException.class }) + public void testAdd_invalidRangeEnd() + { + Range r1 = new Range(10, 20); + NCNode node = new NCNode(r1); + assertEquals(node.getStart(), 10); + Range r2 = new Range(12, 21); + node.add(r2); + } + + @Test(groups = "Functional") + public void testGetEntries() + { + Range r1 = new Range(10, 20); + NCNode node = new NCNode(r1); + List entries = new ArrayList(); + + node.getEntries(entries); + assertEquals(entries.size(), 1); + assertTrue(entries.contains(r1)); + + // clearing the returned list does not affect the NCNode + entries.clear(); + node.getEntries(entries); + assertEquals(entries.size(), 1); + assertTrue(entries.contains(r1)); + + Range r2 = new Range(15, 18); + node.add(r2); + entries.clear(); + node.getEntries(entries); + assertEquals(entries.size(), 2); + assertTrue(entries.contains(r1)); + assertTrue(entries.contains(r2)); + } +} diff --git a/test/jalview/datamodel/features/Range.java b/test/jalview/datamodel/features/Range.java new file mode 100644 index 0000000..701ec2a --- /dev/null +++ b/test/jalview/datamodel/features/Range.java @@ -0,0 +1,32 @@ +package jalview.datamodel.features; + +class Range implements ContiguousI +{ + int start; + + int end; + + @Override + public int getBegin() + { + return start; + } + + @Override + public int getEnd() + { + return end; + } + + Range(int i, int j) + { + start = i; + end = j; + } + + @Override + public String toString() + { + return String.valueOf(start) + "-" + String.valueOf(end); + } +} diff --git a/test/jalview/datamodel/features/SequenceFeaturesTest.java b/test/jalview/datamodel/features/SequenceFeaturesTest.java new file mode 100644 index 0000000..7edd67d --- /dev/null +++ b/test/jalview/datamodel/features/SequenceFeaturesTest.java @@ -0,0 +1,286 @@ +package jalview.datamodel.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import jalview.datamodel.SequenceFeature; + +import java.util.List; + +import org.testng.annotations.Test; + +public class SequenceFeaturesTest +{ + @Test(groups = "Functional") + public void testGetFeatures() + { + SequenceFeatures store = new SequenceFeatures(); + SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.add(sf1); + // same range + SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.add(sf2); + // discontiguous range + SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40, + Float.NaN, null); + store.add(sf3); + // overlapping range + SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35, + Float.NaN, null); + store.add(sf4); + // enclosing range + SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50, + Float.NaN, null); + store.add(sf5); + // non-positional feature + SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0, + Float.NaN, null); + store.add(sf6); + // contact feature + SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc", + 18, 45, Float.NaN, null); + store.add(sf7); + // different feature type + SequenceFeature sf8 = new SequenceFeature("Pfam", "desc", 30, 40, + Float.NaN, null); + store.add(sf8); + SequenceFeature sf9 = new SequenceFeature("Pfam", "desc", 15, 35, + Float.NaN, null); + store.add(sf9); + + /* + * get all features + */ + List features = store.getFeatures(); + assertEquals(features.size(), 9); + assertTrue(features.contains(sf1)); + assertTrue(features.contains(sf2)); + assertTrue(features.contains(sf3)); + assertTrue(features.contains(sf4)); + assertTrue(features.contains(sf5)); + assertTrue(features.contains(sf6)); + assertTrue(features.contains(sf7)); + assertTrue(features.contains(sf8)); + assertTrue(features.contains(sf9)); + + /* + * get features by type + */ + assertTrue(store.getFeatures(null).isEmpty()); + assertTrue(store.getFeatures("Cath").isEmpty()); + assertTrue(store.getFeatures("METAL").isEmpty()); + + features = store.getFeatures("Metal"); + 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)); + + features = store.getFeatures("Disulphide bond"); + assertEquals(features.size(), 1); + assertTrue(features.contains(sf7)); + + features = store.getFeatures("Pfam"); + assertEquals(features.size(), 2); + assertTrue(features.contains(sf8)); + assertTrue(features.contains(sf9)); + } + + @Test(groups = "Functional") + public void testGetContactFeatures() + { + SequenceFeatures store = new SequenceFeatures(); + // non-contact + SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.add(sf1); + // non-positional + SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0, + Float.NaN, null); + store.add(sf2); + // contact feature + SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc", + 18, 45, Float.NaN, null); + store.add(sf3); + // repeat for different feature type + SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20, + Float.NaN, null); + store.add(sf4); + SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0, + Float.NaN, null); + store.add(sf5); + SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18, + 45, Float.NaN, null); + store.add(sf6); + + /* + * get all contact features + */ + List features = store.getContactFeatures(); + assertEquals(features.size(), 2); + assertTrue(features.contains(sf3)); + assertTrue(features.contains(sf6)); + + /* + * get contact features by type + */ + assertTrue(store.getContactFeatures(null).isEmpty()); + assertTrue(store.getContactFeatures("Cath").isEmpty()); + assertTrue(store.getContactFeatures("Pfam").isEmpty()); + assertTrue(store.getContactFeatures("DISULPHIDE BOND").isEmpty()); + + features = store.getContactFeatures("Disulphide bond"); + assertEquals(features.size(), 1); + assertTrue(features.contains(sf3)); + + features = store.getContactFeatures("Disulfide bond"); + assertEquals(features.size(), 1); + assertTrue(features.contains(sf6)); + } + + @Test(groups = "Functional") + public void testGetNonPositionalFeatures() + { + SequenceFeatures store = new SequenceFeatures(); + // positional + SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20, + Float.NaN, null); + store.add(sf1); + // non-positional + SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0, + Float.NaN, null); + store.add(sf2); + // contact feature + SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc", + 18, 45, Float.NaN, null); + store.add(sf3); + // repeat for different feature type + SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20, + Float.NaN, null); + store.add(sf4); + SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0, + Float.NaN, null); + store.add(sf5); + SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18, + 45, Float.NaN, null); + store.add(sf6); + // on more non-positional + SequenceFeature sf7 = new SequenceFeature("Pfam", "desc", 0, 0, + Float.NaN, null); + store.add(sf7); + + /* + * get all non-positional features + */ + List features = store.getNonPositionalFeatures(); + assertEquals(features.size(), 3); + assertTrue(features.contains(sf2)); + assertTrue(features.contains(sf5)); + assertTrue(features.contains(sf7)); + + /* + * get non-positional features by type + */ + assertTrue(store.getNonPositionalFeatures(null).isEmpty()); + assertTrue(store.getNonPositionalFeatures("Cath").isEmpty()); + assertTrue(store.getNonPositionalFeatures("PFAM").isEmpty()); + + features = store.getNonPositionalFeatures("Metal"); + assertEquals(features.size(), 1); + assertTrue(features.contains(sf2)); + + features = store.getNonPositionalFeatures("Pfam"); + assertEquals(features.size(), 2); + assertTrue(features.contains(sf5)); + assertTrue(features.contains(sf7)); + } + + /** + * Helper method to add a feature of no particular type + * + * @param sf + * @param type + * @param from + * @param to + * @return + */ + SequenceFeature addFeature(SequenceFeatures sf, String type, int from, + int to) + { + SequenceFeature sf1 = new SequenceFeature(type, "", from, to, + Float.NaN, + null); + sf.add(sf1); + return sf1; + } + + @Test(groups = "Functional") + public void testFindFeatures() + { + SequenceFeatures sf = new SequenceFeatures(); + SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50); + SequenceFeature sf2 = addFeature(sf, "Pfam", 1, 15); + SequenceFeature sf3 = addFeature(sf, "Pfam", 20, 30); + SequenceFeature sf4 = addFeature(sf, "Pfam", 40, 100); + SequenceFeature sf5 = addFeature(sf, "Pfam", 60, 100); + SequenceFeature sf6 = addFeature(sf, "Pfam", 70, 70); + SequenceFeature sf7 = addFeature(sf, "Cath", 10, 50); + SequenceFeature sf8 = addFeature(sf, "Cath", 1, 15); + SequenceFeature sf9 = addFeature(sf, "Cath", 20, 30); + SequenceFeature sf10 = addFeature(sf, "Cath", 40, 100); + SequenceFeature sf11 = addFeature(sf, "Cath", 60, 100); + SequenceFeature sf12 = addFeature(sf, "Cath", 70, 70); + // null type is weird but possible: + SequenceFeature sf13 = addFeature(sf, null, 5, 12); + + List overlaps = sf.findFeatures("Pfam", 200, 200); + assertTrue(overlaps.isEmpty()); + + overlaps = sf.findFeatures("Pfam", 1, 9); + assertEquals(overlaps.size(), 1); + assertTrue(overlaps.contains(sf2)); + + overlaps = sf.findFeatures("Pfam", 5, 18); + assertEquals(overlaps.size(), 2); + assertTrue(overlaps.contains(sf1)); + assertTrue(overlaps.contains(sf2)); + + overlaps = sf.findFeatures("Pfam", 30, 40); + assertEquals(overlaps.size(), 3); + assertTrue(overlaps.contains(sf1)); + assertTrue(overlaps.contains(sf3)); + assertTrue(overlaps.contains(sf4)); + + overlaps = sf.findFeatures("Pfam", 80, 90); + assertEquals(overlaps.size(), 2); + assertTrue(overlaps.contains(sf4)); + assertTrue(overlaps.contains(sf5)); + + overlaps = sf.findFeatures("Pfam", 68, 70); + assertEquals(overlaps.size(), 3); + assertTrue(overlaps.contains(sf4)); + assertTrue(overlaps.contains(sf5)); + assertTrue(overlaps.contains(sf6)); + + overlaps = sf.findFeatures("Cath", 16, 69); + assertEquals(overlaps.size(), 4); + assertTrue(overlaps.contains(sf7)); + assertFalse(overlaps.contains(sf8)); + assertTrue(overlaps.contains(sf9)); + assertTrue(overlaps.contains(sf10)); + assertTrue(overlaps.contains(sf11)); + assertFalse(overlaps.contains(sf12)); + + assertTrue(sf.findFeatures("Metal", 0, 1000).isEmpty()); + + overlaps = sf.findFeatures(null, 7, 7); + assertEquals(overlaps.size(), 1); + assertTrue(overlaps.contains(sf13)); + } +} -- 1.7.10.2