1 package jalview.util.matcher;
3 import java.util.Objects;
6 * A bean to describe one attribute-based filter
8 public class Matcher implements MatcherI
10 public enum PatternType
12 String, Integer, Float
16 * the comparison condition
18 private final Condition condition;
21 * the string pattern as entered, to compare to
23 private String pattern;
26 * the pattern in upper case, for non-case-sensitive matching
28 private final String uppercasePattern;
31 * the compiled regex if using a pattern match condition
32 * (possible future enhancement)
34 // private Pattern regexPattern;
37 * the value to compare to for a numerical condition with a float pattern
39 private float floatValue = 0F;
42 * the value to compare to for a numerical condition with an integer pattern
44 private long longValue = 0L;
46 private PatternType patternType;
54 * @throws NumberFormatException
55 * if a numerical condition is specified with a non-numeric
57 * @throws NullPointerException
58 * if a null condition or comparison string is specified
60 public Matcher(Condition cond, String compareTo)
62 Objects.requireNonNull(cond);
69 longValue = Long.valueOf(compareTo);
70 pattern = String.valueOf(longValue);
71 patternType = PatternType.Integer;
72 } catch (NumberFormatException e)
74 floatValue = Float.valueOf(compareTo);
75 pattern = String.valueOf(floatValue);
76 patternType = PatternType.Float;
82 patternType = PatternType.String;
85 uppercasePattern = pattern == null ? null : pattern.toUpperCase();
87 // if we add regex conditions (e.g. matchesPattern), then
88 // pattern should hold the raw regex, and
89 // regexPattern = Pattern.compile(compareTo);
93 * Constructor for a float-valued numerical match condition. Note that if a
94 * string comparison condition is specified, this will be converted to a
95 * comparison with the float value as string
100 public Matcher(Condition cond, float compareTo)
102 this(cond, String.valueOf(compareTo));
106 * Constructor for an integer-valued numerical match condition. Note that if a
107 * string comparison condition is specified, this will be converted to a
108 * comparison with the integer value as string
113 public Matcher(Condition cond, long compareTo)
115 this(cond, String.valueOf(compareTo));
122 public boolean matches(String compareTo)
124 if (compareTo == null)
126 return matchesNull();
129 boolean matched = false;
133 matched = matchesFloat(compareTo, floatValue);
136 matched = matchesLong(compareTo);
139 matched = matchesString(compareTo);
146 * Executes a non-case-sensitive string comparison to the given value, after
147 * trimming it. Returns true if the test passes, false if it fails.
152 boolean matchesString(String compareTo)
154 boolean matched = false;
155 String upper = compareTo.toUpperCase().trim();
158 matched = upper.equals(uppercasePattern);
161 matched = !upper.equals(uppercasePattern);
164 matched = upper.indexOf(uppercasePattern) > -1;
167 matched = upper.indexOf(uppercasePattern) == -1;
179 * Performs a numerical comparison match condition test against a float value
185 boolean matchesFloat(String testee, float compareTo)
187 if (!condition.isNumeric())
189 // failsafe, shouldn't happen
190 return matches(testee);
196 f = Float.valueOf(testee);
197 } catch (NumberFormatException e)
202 boolean matched = false;
205 matched = f < compareTo;
208 matched = f <= compareTo;
211 matched = f == compareTo;
214 matched = f != compareTo;
217 matched = f > compareTo;
220 matched = f >= compareTo;
230 * A simple hash function that guarantees that when two objects are equal,
231 * they have the same hashcode
234 public int hashCode()
236 return pattern.hashCode() + condition.hashCode() + (int) floatValue;
240 * equals is overridden so that we can safely remove Matcher objects from
241 * collections (e.g. delete an attribute match condition for a feature colour)
244 public boolean equals(Object obj)
246 if (obj == null || !(obj instanceof Matcher))
250 Matcher m = (Matcher) obj;
251 if (condition != m.condition || floatValue != m.floatValue
252 || longValue != m.longValue)
258 return m.pattern == null;
260 return uppercasePattern.equals(m.uppercasePattern);
264 public Condition getCondition()
270 public String getPattern()
276 public String toString()
278 StringBuilder sb = new StringBuilder();
279 sb.append(condition.toString()).append(" ");
280 if (condition.isNumeric())
286 sb.append("'").append(pattern).append("'");
289 return sb.toString();
293 * Performs a numerical comparison match condition test against an integer
299 boolean matchesLong(String compareTo)
301 if (!condition.isNumeric())
303 // failsafe, shouldn't happen
304 return matches(String.valueOf(compareTo));
310 val = Long.valueOf(compareTo);
311 } catch (NumberFormatException e)
314 * try the presented value as a float instead
316 return matchesFloat(compareTo, longValue);
319 boolean matched = false;
322 matched = val < longValue;
325 matched = val <= longValue;
328 matched = val == longValue;
331 matched = val != longValue;
334 matched = val > longValue;
337 matched = val >= longValue;
347 * Tests whether a null value matches the condition. The rule is that any
348 * numeric condition is failed, and only 'negative' string conditions are
349 * matched. So for example <br>
350 * {@code null contains "damaging"}<br>
352 * {@code null does not contain "damaging"}</br>
355 boolean matchesNull()
357 if (condition.isNumeric())
363 return condition == Condition.NotContains
364 || condition == Condition.NotMatches
365 || condition == Condition.NotPresent;