JAL-2808 refactor KeyedMatcher as FeatureMatcher with byLabel, byScore, byAttribute...
[jalview.git] / src / jalview / datamodel / features / FeatureMatcher.java
diff --git a/src/jalview/datamodel/features/FeatureMatcher.java b/src/jalview/datamodel/features/FeatureMatcher.java
new file mode 100644 (file)
index 0000000..1fc0e0f
--- /dev/null
@@ -0,0 +1,139 @@
+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();
+  }
+}