+ 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 pos = BinarySearcher.findFirst(features,
+ val -> val.getBegin() >= feature.getBegin());
+ 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 start
+ * start position of overlap range (inclusive)
+ * @param end
+ * end position of overlap range (inclusive)
+ * @return
+ */
+ public List<SequenceFeature> findOverlappingFeatures(long start, long end)
+ {
+ List<SequenceFeature> result = new ArrayList<>();
+
+ findContactFeatures(start, end, result);
+
+ if (features != null)
+ {
+ result.addAll(features.findOverlaps(start, end));
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds contact features to the result list where either the second or the
+ * first contact position lies within the target range
+ *
+ * @param from
+ * @param to
+ * @param result
+ */
+ protected void findContactFeatures(long from, long to,
+ List<SequenceFeature> result)
+ {
+ if (contactFeatureStarts != null)
+ {
+ findContactStartOverlaps(from, to, result);
+ }
+ if (contactFeatureEnds != null)
+ {
+ findContactEndOverlaps(from, to, result);
+ }
+ }
+
+ /**
+ * Adds to the result list any contact features whose end (second contact
+ * point), but not start (first contact point), lies in the query from-to
+ * range
+ *
+ * @param from
+ * @param to
+ * @param result
+ */
+ protected void findContactEndOverlaps(long from, long to,
+ List<SequenceFeature> result)
+ {
+ /*
+ * find the first contact feature (if any)
+ * whose end point is not before the target range
+ */
+ int index = BinarySearcher.findFirst(contactFeatureEnds,
+ f -> f.getEnd() >= from);
+
+ while (index < contactFeatureEnds.size())
+ {
+ SequenceFeature sf = contactFeatureEnds.get(index);
+ if (!sf.isContactFeature())
+ {
+ System.err.println("Error! non-contact feature type "
+ + sf.getType() + " in contact features list");
+ index++;
+ continue;
+ }
+
+ int begin = sf.getBegin();
+ if (begin >= from && begin <= to)
+ {
+ /*
+ * this feature's first contact position lies in the search range
+ * so we don't include it in results a second time
+ */
+ index++;
+ continue;
+ }
+
+ if (sf.getEnd() > to)
+ {
+ /*
+ * this feature (and all following) has end point after the target range
+ */
+ break;
+ }
+
+ /*
+ * feature has end >= from and end <= to
+ * i.e. contact end point lies within overlap search range
+ */
+ result.add(sf);
+ index++;
+ }
+ }
+
+ /**
+ * Adds contact features whose start position lies in the from-to range to the
+ * result list
+ *
+ * @param from
+ * @param to
+ * @param result
+ */
+ protected void findContactStartOverlaps(long from, long to,
+ List<SequenceFeature> result)
+ {
+ int index = BinarySearcher.findFirst(contactFeatureStarts,
+ f -> f.getBegin() >= from);
+
+ while (index < contactFeatureStarts.size())
+ {
+ SequenceFeature sf = contactFeatureStarts.get(index);
+ if (!sf.isContactFeature())
+ {
+ System.err.println("Error! non-contact feature " + sf.toString()
+ + " in contact features list");
+ index++;
+ continue;
+ }
+ if (sf.getBegin() > to)
+ {
+ /*
+ * this feature's start (and all following) follows the target range
+ */
+ break;
+ }
+
+ /*
+ * feature has begin >= from and begin <= to
+ * i.e. contact start point lies within overlap search range
+ */
+ result.add(sf);
+ index++;
+ }