- // we only construct nonPositionalFeatures, contactFeatures
- // or the NCList if we need to
- }
-
- /**
- * Adds one sequence feature to the store, and returns true, unless the
- * feature is already contained in the store, in which case this method
- * returns false. Containment is determined by SequenceFeature.equals()
- * comparison.
- *
- * @param feature
- */
- public boolean addFeature(SequenceFeature feature)
- {
- if (contains(feature))
- {
- return false;
- }
-
- /*
- * keep a record of feature groups
- */
- if (!feature.isNonPositional())
- {
- positionalFeatureGroups.add(feature.getFeatureGroup());
- }
-
- boolean added = false;
-
- if (feature.isContactFeature())
- {
- added = addContactFeature(feature);
- }
- else if (feature.isNonPositional())
- {
- added = addNonPositionalFeature(feature);
- }
- else
- {
- added = addNonNestedFeature(feature);
- if (!added)
- {
- /*
- * detected a nested feature - put it in the NCList structure
- */
- added = addNestedFeature(feature);
- }
- }
-
- if (added)
- {
- /*
- * record the total extent of positional features, to make
- * getTotalFeatureLength possible; we count the length of a
- * contact feature as 1
- */
- totalExtent += getFeatureLength(feature);
-
- /*
- * record the minimum and maximum score for positional
- * and non-positional features
- */
- float score = feature.getScore();
- if (!Float.isNaN(score))
- {
- if (feature.isNonPositional())
- {
- nonPositionalMinScore = min(nonPositionalMinScore, score);
- nonPositionalMaxScore = max(nonPositionalMaxScore, score);
- }
- else
- {
- positionalMinScore = min(positionalMinScore, score);
- positionalMaxScore = max(positionalMaxScore, score);
- }
- }
- }
-
- return added;
- }
-
- /**
- * Answers true if this store contains the given feature (testing by
- * SequenceFeature.equals), else false
- *
- * @param feature
- * @return
- */
- public boolean contains(SequenceFeature feature)
- {
- if (feature.isNonPositional())
- {
- return nonPositionalFeatures == null ? false : nonPositionalFeatures
- .contains(feature);
- }
-
- if (feature.isContactFeature())
- {
- return contactFeatureStarts == null ? false : listContains(
- contactFeatureStarts, feature);
- }
-
- if (listContains(nonNestedFeatures, feature))
- {
- return true;
- }
-
- return nestedFeatures == null ? false : nestedFeatures
- .contains(feature);
- }
-
- /**
- * Answers the 'length' of the feature, counting 0 for non-positional features
- * and 1 for contact features
- *
- * @param feature
- * @return
- */
- protected static int getFeatureLength(SequenceFeature feature)
- {
- if (feature.isNonPositional())
- {
- return 0;
- }
- if (feature.isContactFeature())
- {
- return 1;
- }
- return 1 + feature.getEnd() - feature.getBegin();
- }
-
- /**
- * Adds the feature to the list of non-positional features (with lazy
- * instantiation of the list if it is null), and returns true. The feature
- * group is added to the set of distinct feature groups for non-positional
- * features. This method allows duplicate features, so test before calling to
- * prevent this.
- *
- * @param feature
- */
- protected boolean addNonPositionalFeature(SequenceFeature feature)
- {
- if (nonPositionalFeatures == null)
- {
- nonPositionalFeatures = new ArrayList<SequenceFeature>();
- }
-
- nonPositionalFeatures.add(feature);
-
- nonPositionalFeatureGroups.add(feature.getFeatureGroup());
-
- return true;
- }
-
- /**
- * Adds one feature to the NCList that can manage nested features (creating
- * the NCList if necessary), and returns true. If the feature is already
- * stored in the NCList (by equality test), then it is not added, and this
- * method returns false.
- */
- protected synchronized boolean addNestedFeature(SequenceFeature feature)
- {
- if (nestedFeatures == null)
- {
- nestedFeatures = new NCList<>(feature);
- return true;
- }
- return nestedFeatures.add(feature, false);
- }
-
- /**
- * Add a feature to the list of non-nested features, maintaining the ordering
- * of the list. A check is made for whether the feature is nested in (properly
- * contained by) an existing feature. If there is no nesting, the feature is
- * added to the list and the method returns true. If nesting is found, the
- * feature is not added and the method returns false.
- *
- * @param feature
- * @return
- */
- protected boolean addNonNestedFeature(SequenceFeature feature)
- {
- synchronized (nonNestedFeatures)
- {
- /*
- * find the first stored feature which doesn't precede the new one
- */
- int insertPosition = binarySearch(nonNestedFeatures,
- SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION));
-
- /*
- * fail if we detect feature enclosure - of the new feature by
- * the one preceding it, or of the next feature by the new one
- */
- if (insertPosition > 0)
- {
- if (encloses(nonNestedFeatures.get(insertPosition - 1), feature))
- {
- return false;
- }
- }
- if (insertPosition < nonNestedFeatures.size())
- {
- if (encloses(feature, nonNestedFeatures.get(insertPosition)))
- {
- return false;
- }
- }
-
- /*
- * checks passed - add the feature
- */
- nonNestedFeatures.add(insertPosition, feature);
-
- return true;
- }
- }
-
- /**
- * Answers true if range1 properly encloses range2, else false
- *
- * @param range1
- * @param range2
- * @return
- */
- protected boolean encloses(ContiguousI range1, ContiguousI range2)
- {
- int begin1 = range1.getBegin();
- int begin2 = range2.getBegin();
- int end1 = range1.getEnd();
- int end2 = range2.getEnd();
- if (begin1 == begin2 && end1 > end2)
- {
- return true;
- }
- if (begin1 < begin2 && end1 >= end2)
- {
- return true;
- }
- return false;