+ return result;
+ }
+
+ if (positional)
+ {
+ addFeaturesForGroup(group, contactFeatureStarts, result);
+ addFeaturesForGroup(group, features, result);
+ }
+ else
+ {
+ addFeaturesForGroup(group, nonPositionalFeatures, result);
+ }
+ return result;
+ }
+
+ /**
+ * Answers the maximum score held for positional or non-positional features.
+ * This may be Float.NaN if there are no features, are none has a non-NaN
+ * score.
+ *
+ * @param positional
+ * @return
+ */
+ public float getMaximumScore(boolean positional)
+ {
+ return positional ? positionalMaxScore : nonPositionalMaxScore;
+ }
+
+ /**
+ * Answers the minimum score held for positional or non-positional features.
+ * This may be Float.NaN if there are no features, are none has a non-NaN
+ * score.
+ *
+ * @param positional
+ * @return
+ */
+ public float getMinimumScore(boolean positional)
+ {
+ return positional ? positionalMinScore : nonPositionalMinScore;
+ }
+
+ public List<SequenceFeature> getNonPositionalFeatures()
+ {
+ return getNonPositionalFeatures(new ArrayList<>());
+ }
+
+ /**
+ * Answers a list of all non-positional features. If there are none, returns
+ * an immutable empty list.
+ *
+ * @return
+ */
+ public List<SequenceFeature> getNonPositionalFeatures(
+ List<SequenceFeature> result)
+ {
+ if (nonPositionalFeatures != null)
+ {
+ result.addAll(nonPositionalFeatures);
+ }
+ return result;
+ }
+
+ public List<SequenceFeature> getPositionalFeatures()
+ {
+ return getPositionalFeatures(new ArrayList<>());
+ }
+
+ /**
+ * Answers a list of all positional features stored, in no guaranteed order
+ *
+ * @return
+ */
+ public List<SequenceFeature> getPositionalFeatures(
+ List<SequenceFeature> result)
+ {
+
+ /*
+ * add any contact features - from the list by start position
+ */
+ if (contactFeatureStarts != null)
+ {
+ result.addAll(contactFeatureStarts);
+ }
+
+ /*
+ * add any nested features
+ */
+ if (features != null)
+ {
+ result.addAll(features);
+ }
+
+ return result;
+ }
+
+ /**
+ * Answers the total length of positional features (or zero if there are
+ * none). Contact features contribute a value of 1 to the total.
+ *
+ * @return
+ */
+ public int getTotalFeatureLength()
+ {
+ return totalExtent;
+ }
+
+ /**
+ * Answers true if this store has no features, else false
+ *
+ * @return
+ */
+ public boolean isEmpty()
+ {
+ boolean hasFeatures = (contactFeatureStarts != null
+ && !contactFeatureStarts.isEmpty())
+ || (nonPositionalFeatures != null
+ && !nonPositionalFeatures.isEmpty())
+ || features.size() > 0;
+
+ return !hasFeatures;
+ }
+
+ /**
+ * Rescan all features to recompute any cached values after an entry has been
+ * deleted. This is expected to be an infrequent event, so performance here is
+ * not critical.
+ */
+ protected synchronized void rescanAfterDelete()
+ {
+ positionalFeatureGroups.clear();
+ nonPositionalFeatureGroups.clear();
+ totalExtent = 0;
+ positionalMinScore = Float.NaN;
+ positionalMaxScore = Float.NaN;
+ nonPositionalMinScore = Float.NaN;
+ nonPositionalMaxScore = Float.NaN;
+ /*
+ * scan non-positional features for groups and scores
+ */
+ if (nonPositionalFeatures != null)
+ {
+ List<SequenceFeature> list = nonPositionalFeatures;
+ for (int i = 0, n = list.size(); i < n; i++)