+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
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
{
+ public enum PatternType
+ {
+ String, Integer, Float
+ }
+
/*
* the comparison condition
*/
- Condition condition;
+ private final 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
+ * the string pattern as entered, to compare to
*/
- String pattern;
+ private String pattern;
/*
* the pattern in upper case, for non-case-sensitive matching
*/
- String uppercasePattern;
+ private final String uppercasePattern;
/*
* the compiled regex if using a pattern match condition
- * (reserved for possible future enhancement)
+ * (possible future enhancement)
+ */
+ // private Pattern regexPattern;
+
+ /*
+ * the value to compare to for a numerical condition with a float pattern
*/
- Pattern regexPattern;
+ private float floatValue = 0F;
/*
- * the value to compare to for a numerical condition
+ * the value to compare to for a numerical condition with an integer pattern
*/
- float value;
+ private long longValue = 0L;
+
+ private PatternType patternType;
/**
* Constructor
{
Objects.requireNonNull(cond);
condition = cond;
+
if (cond.isNumeric())
{
- value = Float.valueOf(compareTo);
- pattern = String.valueOf(value);
- uppercasePattern = pattern;
+ try
+ {
+ longValue = Long.valueOf(compareTo);
+ pattern = String.valueOf(longValue);
+ patternType = PatternType.Integer;
+ } catch (NumberFormatException e)
+ {
+ floatValue = Float.valueOf(compareTo);
+ pattern = String.valueOf(floatValue);
+ patternType = PatternType.Float;
+ }
}
else
{
pattern = compareTo;
- if (pattern != null)
- {
- uppercasePattern = pattern.toUpperCase();
- }
+ patternType = PatternType.String;
}
+ uppercasePattern = pattern == null ? null : 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
+ * Constructor for a float-valued 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
}
/**
+ * Constructor for an integer-valued numerical match condition. Note that if a
+ * string comparison condition is specified, this will be converted to a
+ * comparison with the integer value as string
+ *
+ * @param cond
+ * @param compareTo
+ */
+ public Matcher(Condition cond, long compareTo)
+ {
+ this(cond, String.valueOf(compareTo));
+ }
+
+ /**
* {@inheritDoc}
*/
- @SuppressWarnings("incomplete-switch")
@Override
- public boolean matches(String val)
+ public boolean matches(String compareTo)
{
- if (condition.isNumeric())
+ if (compareTo == null)
{
- 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;
- }
+ return matchesNull();
}
-
- /*
- * a null value matches a negative condition, fails a positive test
- */
- if (val == null)
+
+ boolean matched = false;
+ switch (patternType)
{
- return condition == Condition.NotContains
- || condition == Condition.NotMatches
- || condition == Condition.NotPresent;
+ case Float:
+ matched = matchesFloat(compareTo, floatValue);
+ break;
+ case Integer:
+ matched = matchesLong(compareTo);
+ break;
+ default:
+ matched = matchesString(compareTo);
+ break;
}
-
- String upper = val.toUpperCase().trim();
+ return matched;
+ }
+
+ /**
+ * Executes a non-case-sensitive string comparison to the given value, after
+ * trimming it. Returns true if the test passes, false if it fails.
+ *
+ * @param compareTo
+ * @return
+ */
+ boolean matchesString(String compareTo)
+ {
boolean matched = false;
+ String upper = compareTo.toUpperCase().trim();
switch(condition) {
case Matches:
matched = upper.equals(uppercasePattern);
}
/**
- * Applies a numerical comparison match condition
+ * Performs a numerical comparison match condition test against a float value
*
- * @param f
+ * @param testee
+ * @param compareTo
* @return
*/
- @SuppressWarnings("incomplete-switch")
- boolean matches(float f)
+ boolean matchesFloat(String testee, float compareTo)
{
if (!condition.isNumeric())
{
- return matches(String.valueOf(f));
+ // failsafe, shouldn't happen
+ return matches(testee);
+ }
+
+ float f = 0f;
+ try
+ {
+ f = Float.valueOf(testee);
+ } catch (NumberFormatException e)
+ {
+ return false;
}
boolean matched = false;
switch (condition) {
case LT:
- matched = f < value;
+ matched = f < compareTo;
break;
case LE:
- matched = f <= value;
+ matched = f <= compareTo;
break;
case EQ:
- matched = f == value;
+ matched = f == compareTo;
break;
case NE:
- matched = f != value;
+ matched = f != compareTo;
break;
case GT:
- matched = f > value;
+ matched = f > compareTo;
break;
case GE:
- matched = f >= value;
+ matched = f >= compareTo;
break;
default:
break;
@Override
public int hashCode()
{
- return pattern.hashCode() + condition.hashCode() + (int) value;
+ return pattern.hashCode() + condition.hashCode() + (int) floatValue;
}
/**
return false;
}
Matcher m = (Matcher) obj;
- if (condition != m.condition || value != m.value)
+ if (condition != m.condition || floatValue != m.floatValue
+ || longValue != m.longValue)
{
return false;
}
}
@Override
- public float getFloatValue()
- {
- return value;
- }
-
- @Override
public String toString()
{
StringBuilder sb = new StringBuilder();
return sb.toString();
}
+
+ /**
+ * Performs a numerical comparison match condition test against an integer
+ * value
+ *
+ * @param compareTo
+ * @return
+ */
+ boolean matchesLong(String compareTo)
+ {
+ if (!condition.isNumeric())
+ {
+ // failsafe, shouldn't happen
+ return matches(String.valueOf(compareTo));
+ }
+
+ long val = 0L;
+ try
+ {
+ val = Long.valueOf(compareTo);
+ } catch (NumberFormatException e)
+ {
+ /*
+ * try the presented value as a float instead
+ */
+ return matchesFloat(compareTo, longValue);
+ }
+
+ boolean matched = false;
+ switch (condition) {
+ case LT:
+ matched = val < longValue;
+ break;
+ case LE:
+ matched = val <= longValue;
+ break;
+ case EQ:
+ matched = val == longValue;
+ break;
+ case NE:
+ matched = val != longValue;
+ break;
+ case GT:
+ matched = val > longValue;
+ break;
+ case GE:
+ matched = val >= longValue;
+ break;
+ default:
+ break;
+ }
+
+ return matched;
+ }
+
+ /**
+ * Tests whether a null value matches the condition. The rule is that any
+ * numeric condition is failed, and only 'negative' string conditions are
+ * matched. So for example <br>
+ * {@code null contains "damaging"}<br>
+ * fails, but <br>
+ * {@code null does not contain "damaging"}</br>
+ * passes.
+ */
+ boolean matchesNull()
+ {
+ if (condition.isNumeric())
+ {
+ return false;
+ }
+ else
+ {
+ return condition == Condition.NotContains
+ || condition == Condition.NotMatches
+ || condition == Condition.NotPresent;
+ }
+ }
}