--- /dev/null
+package jalview.datamodel.features;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.util.matcher.Condition;
+import jalview.util.matcher.Matcher;
+import jalview.util.matcher.MatcherI;
+
+/**
+ * An immutable class that models one or more match conditions, each of which is
+ * applied to the value obtained by lookup given the match key.
+ * <p>
+ * For example, the value provider could be a SequenceFeature's attributes map,
+ * and the conditions might be
+ * <ul>
+ * <li>CSQ contains "pathological"</li>
+ * <li>AND</li>
+ * <li>AF <= 1.0e-5</li>
+ * </ul>
+ *
+ * @author gmcarstairs
+ *
+ */
+public class FeatureMatcher implements FeatureMatcherI
+{
+ private static final String COLON = ":";
+
+ /*
+ * if true, match is against feature description
+ */
+ final private boolean byLabel;
+
+ /*
+ * if true, match is against feature score
+ */
+ final private boolean byScore;
+
+ /*
+ * if not null, match is against feature attribute [sub-attribute]
+ */
+ final private String[] key;
+
+ final private MatcherI matcher;
+
+ /**
+ * A factory constructor method for a matcher that applies its match condition
+ * to the feature label (description)
+ *
+ * @param cond
+ * @param pattern
+ * @return
+ */
+ public static FeatureMatcher byLabel(Condition cond, String pattern)
+ {
+ return new FeatureMatcher(new Matcher(cond, pattern), true, false,
+ null);
+ }
+
+ /**
+ * A factory constructor method for a matcher that applies its match condition
+ * to the feature score
+ *
+ * @param cond
+ * @param pattern
+ * @return
+ */
+ public static FeatureMatcher byScore(Condition cond, String pattern)
+ {
+ return new FeatureMatcher(new Matcher(cond, pattern), false, true,
+ null);
+ }
+
+ /**
+ * A factory constructor method for a matcher that applies its match condition
+ * to the named feature attribute [and optional sub-attribute]
+ *
+ * @param cond
+ * @param pattern
+ * @param attName
+ * @return
+ */
+ public static FeatureMatcher byAttribute(Condition cond, String pattern,
+ String... attName)
+ {
+ return new FeatureMatcher(new Matcher(cond, pattern), false, false,
+ attName);
+ }
+
+ private FeatureMatcher(Matcher m, boolean forLabel, boolean forScore,
+ String[] theKey)
+ {
+ key = theKey;
+ matcher = m;
+ byLabel = forLabel;
+ byScore = forScore;
+ }
+ @Override
+ public boolean matches(SequenceFeature feature)
+ {
+ String value = byLabel ? feature.getDescription()
+ : (byScore ? String.valueOf(feature.getScore())
+ : feature.getValueAsString(key));
+ return matcher.matches(value);
+ }
+
+ @Override
+ public String[] getKey()
+ {
+ return key;
+ }
+
+ @Override
+ public MatcherI getMatcher()
+ {
+ return matcher;
+ }
+
+ /**
+ * Answers a string description of this matcher, suitable for display, debugging
+ * or logging. The format may change in future.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.join(COLON, key)).append(" ")
+ .append(matcher.getCondition().toString());
+ Condition condition = matcher.getCondition();
+ if (condition.isNumeric())
+ {
+ sb.append(" ").append(matcher.getPattern());
+ }
+ else if (condition.needsAPattern())
+ {
+ sb.append(" '").append(matcher.getPattern()).append("'");
+ }
+
+ return sb.toString();
+ }
+}