package jalview.util.matcher; import java.util.Objects; import java.util.regex.Pattern; /** * A bean to describe one attribute-based filter */ public class Matcher implements MatcherI { /* * the comparison condition */ Condition condition; /* * the string pattern as entered, or the regex, to compare to * also holds the string form of float value if a numeric condition */ String pattern; /* * the pattern in upper case, for non-case-sensitive matching */ String uppercasePattern; /* * the compiled regex if using a pattern match condition * (reserved for possible future enhancement) */ Pattern regexPattern; /* * the value to compare to for a numerical condition */ float value; /** * Constructor * * @param cond * @param compareTo * @return * @throws NumberFormatException * if a numerical condition is specified with a non-numeric * comparison value * @throws NullPointerException * if a null condition or comparison string is specified */ public Matcher(Condition cond, String compareTo) { Objects.requireNonNull(cond); condition = cond; if (cond.isNumeric()) { value = Float.valueOf(compareTo); pattern = String.valueOf(value); uppercasePattern = pattern; } else { pattern = compareTo; if (pattern != null) { uppercasePattern = pattern.toUpperCase(); } } // if we add regex conditions (e.g. matchesPattern), then // pattern should hold the raw regex, and // regexPattern = Pattern.compile(compareTo); } /** * Constructor for a numerical match condition. Note that if a string * comparison condition is specified, this will be converted to a comparison * with the float value as string * * @param cond * @param compareTo */ public Matcher(Condition cond, float compareTo) { this(cond, String.valueOf(compareTo)); } /** * {@inheritDoc} */ @SuppressWarnings("incomplete-switch") @Override public boolean matches(String val) { if (condition.isNumeric()) { try { /* * treat a null value (no such attribute) as * failing any numerical filter condition */ return val == null ? false : matches(Float.valueOf(val)); } catch (NumberFormatException e) { return false; } } /* * a null value matches a negative condition, fails a positive test */ if (val == null) { return condition == Condition.NotContains || condition == Condition.NotMatches || condition == Condition.NotPresent; } String upper = val.toUpperCase().trim(); boolean matched = false; switch(condition) { case Matches: matched = upper.equals(uppercasePattern); break; case NotMatches: matched = !upper.equals(uppercasePattern); break; case Contains: matched = upper.indexOf(uppercasePattern) > -1; break; case NotContains: matched = upper.indexOf(uppercasePattern) == -1; break; case Present: matched = true; break; default: break; } return matched; } /** * Applies a numerical comparison match condition * * @param f * @return */ @SuppressWarnings("incomplete-switch") boolean matches(float f) { if (!condition.isNumeric()) { return matches(String.valueOf(f)); } boolean matched = false; switch (condition) { case LT: matched = f < value; break; case LE: matched = f <= value; break; case EQ: matched = f == value; break; case NE: matched = f != value; break; case GT: matched = f > value; break; case GE: matched = f >= value; break; default: break; } return matched; } /** * A simple hash function that guarantees that when two objects are equal, * they have the same hashcode */ @Override public int hashCode() { return pattern.hashCode() + condition.hashCode() + (int) value; } /** * equals is overridden so that we can safely remove Matcher objects from * collections (e.g. delete an attribute match condition for a feature colour) */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Matcher)) { return false; } Matcher m = (Matcher) obj; if (condition != m.condition || value != m.value) { return false; } if (pattern == null) { return m.pattern == null; } return uppercasePattern.equals(m.uppercasePattern); } @Override public Condition getCondition() { return condition; } @Override public String getPattern() { return pattern; } @Override public float getFloatValue() { return value; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(condition.toString()).append(" "); if (condition.isNumeric()) { sb.append(pattern); } else { sb.append("'").append(pattern).append("'"); } return sb.toString(); } }