}
}
- Comparator<ContiguousI> startOrdering = new RangeComparator(true);
-
- Comparator<ContiguousI> endOrdering = new RangeComparator(false);
-
/*
* Non-positional features have no (zero) start/end position.
* Kept as a separate list in case this criterion changes in future.
}
else
{
- if (!nonNestedFeatures.contains(feature))
+ if (!contains(nonNestedFeatures, feature))
{
added = addNonNestedFeature(feature);
if (!added)
* find the first stored feature which doesn't precede the new one
*/
int insertPosition = binarySearch(nonNestedFeatures,
- SearchCriterion.byFeature(feature, startOrdering));
+ SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION));
/*
* fail if we detect feature enclosure - of the new feature by
contactFeatureEnds = new ArrayList<SequenceFeature>();
}
- // TODO binary search for insertion points!
- if (contactFeatureStarts.contains(feature))
+ if (contains(contactFeatureStarts, feature))
{
return false;
}
- contactFeatureStarts.add(feature);
- Collections.sort(contactFeatureStarts, startOrdering);
+ /*
+ * binary search the sorted list to find the insertion point
+ */
+ int insertPosition = binarySearch(contactFeatureStarts,
+ SearchCriterion.byFeature(feature,
+ RangeComparator.BY_START_POSITION));
+ contactFeatureStarts.add(insertPosition, feature);
+ // and resort to mak siccar...just in case insertion point not quite right
+ Collections.sort(contactFeatureStarts, RangeComparator.BY_START_POSITION);
+
+ insertPosition = binarySearch(contactFeatureStarts,
+ SearchCriterion.byFeature(feature,
+ RangeComparator.BY_END_POSITION));
contactFeatureEnds.add(feature);
- Collections.sort(contactFeatureEnds, endOrdering);
+ Collections.sort(contactFeatureEnds, RangeComparator.BY_END_POSITION);
return true;
}
/**
+ * Answers true if the list contains the feature, else false. This method is
+ * optimised for the condition that the list is sorted on feature start
+ * position ascending, and will give unreliable results if this does not hold.
+ *
+ * @param features
+ * @param feature
+ * @return
+ */
+ protected static boolean contains(List<SequenceFeature> features,
+ SequenceFeature feature)
+ {
+ if (features == null || feature == null)
+ {
+ return false;
+ }
+
+ /*
+ * locate the first entry in the list which does not precede the feature
+ */
+ int pos = binarySearch(features,
+ SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION));
+ int len = features.size();
+ while (pos < len)
+ {
+ SequenceFeature sf = features.get(pos);
+ if (sf.getBegin() > feature.getBegin())
+ {
+ return false; // no match found
+ }
+ if (sf.equals(feature))
+ {
+ return true;
+ }
+ pos++;
+ }
+ return false;
+ }
+
+ /**
* Returns a (possibly empty) list of features whose extent overlaps the given
* range. The returned list is not ordered. Contact features are included if
* either of the contact points lies within the range.
* @param sc
* @return
*/
- protected int binarySearch(List<SequenceFeature> features,
+ protected static int binarySearch(List<SequenceFeature> features,
SearchCriterion sc)
{
int start = 0;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
/**
*/
private List<NCNode<T>> subranges;
- /*
- * a comparator to sort intervals by start position ascending, with
- * longer (enclosing) intervals preceding those they enclose
- */
- Comparator<ContiguousI> intervalSorter = new RangeComparator(true);
-
/**
* Constructor given a list of things that are each located on a contiguous
* interval. Note that the constructor may reorder the list.
* sort by start ascending so that contained intervals
* follow their containing interval
*/
- Collections.sort(ranges, intervalSorter);
+ Collections.sort(ranges, RangeComparator.BY_START_POSITION);
List<Range> sublists = buildSubranges(ranges);
if (subRegions != null)
{
subranges.addAll(subRegions.subranges);
- Collections.sort(subranges, intervalSorter);
+ Collections.sort(subranges, RangeComparator.BY_START_POSITION);
}
size--;
return true;
/**
* A comparator that orders ranges by either start position or end position
- * ascending. If the position matches,
+ * ascending. If the position matches, ordering is resolved by end position (or
+ * start position).
*
* @author gmcarstairs
*
*/
public class RangeComparator implements Comparator<ContiguousI>
{
+ public static final Comparator<ContiguousI> BY_START_POSITION = new RangeComparator(
+ true);
+
+ public static final Comparator<ContiguousI> BY_END_POSITION = new RangeComparator(
+ false);
+
boolean byStart;
+ /**
+ * Constructor
+ *
+ * @param byStartPosition
+ * if true, order based on start position, if false by end position
+ */
+ RangeComparator(boolean byStartPosition)
+ {
+ byStart = byStartPosition;
+ }
+
@Override
public int compare(ContiguousI o1, ContiguousI o2)
{
}
return order;
}
-
- /**
- * Constructor
- *
- * @param byStartPosition
- * if true, order based on start position, if false by end position
- */
- public RangeComparator(boolean byStartPosition)
- {
- byStart = byStartPosition;
- }
}
import jalview.datamodel.SequenceFeature;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
assertEquals(fs.getMinimumScore(false), Float.NaN);
assertEquals(fs.getMaximumScore(false), Float.NaN);
}
+
+ @Test(groups = "Functional")
+ public void testContains()
+ {
+ assertFalse(FeatureStore.contains(null, null));
+ List<SequenceFeature> features = new ArrayList<SequenceFeature>();
+ assertFalse(FeatureStore.contains(features, null));
+
+ SequenceFeature sf1 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
+ "group1");
+ assertFalse(FeatureStore.contains(null, sf1));
+ assertFalse(FeatureStore.contains(features, sf1));
+
+ features.add(sf1);
+ SequenceFeature sf2 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
+ "group1");
+ SequenceFeature sf3 = new SequenceFeature("type1", "desc1", 20, 40, 3f,
+ "group1");
+
+ // sf2.equals(sf1) so contains should return true
+ assertTrue(FeatureStore.contains(features, sf2));
+ assertFalse(FeatureStore.contains(features, sf3));
+ }
}
import static org.testng.Assert.assertEquals;
+import java.util.Comparator;
+
import org.testng.annotations.Test;
public class RangeComparatorTest
{
- class Range implements ContiguousI
- {
- int begin;
-
- int end;
-
- @Override
- public int getBegin()
- {
- return begin;
- }
-
- @Override
- public int getEnd()
- {
- return end;
- }
-
- Range(int i, int j)
- {
- begin = i;
- end = j;
- }
- }
@Test(groups = "Functional")
public void testCompare()
@Test(groups = "Functional")
public void testCompare_byStart()
{
- RangeComparator comp = new RangeComparator(true);
+ Comparator<ContiguousI> comp = RangeComparator.BY_START_POSITION;
// same start position, same length
assertEquals(comp.compare(new Range(10, 20), new Range(10, 20)), 0);
@Test(groups = "Functional")
public void testCompare_byEnd()
{
- RangeComparator comp = new RangeComparator(false);
+ Comparator<ContiguousI> comp = RangeComparator.BY_END_POSITION;
// same end position, same length
assertEquals(comp.compare(new Range(10, 20), new Range(10, 20)), 0);