2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.util.matcher;
23 import java.util.Locale;
25 import java.util.Objects;
28 * A bean to describe one attribute-based filter
30 public class Matcher implements MatcherI
32 public enum PatternType
34 String, Integer, Float
38 * the comparison condition
40 private final Condition condition;
43 * the string pattern as entered, to compare to
45 private String pattern;
48 * the pattern in upper case, for non-case-sensitive matching
50 private final String uppercasePattern;
53 * the compiled regex if using a pattern match condition
54 * (possible future enhancement)
56 // private Pattern regexPattern;
59 * the value to compare to for a numerical condition with a float pattern
61 private float floatValue = 0F;
64 * the value to compare to for a numerical condition with an integer pattern
66 private long longValue = 0L;
68 private PatternType patternType;
76 * @throws NumberFormatException
77 * if a numerical condition is specified with a non-numeric
79 * @throws NullPointerException
80 * if a null condition or comparison string is specified
82 public Matcher(Condition cond, String compareTo)
84 Objects.requireNonNull(cond);
91 longValue = Long.valueOf(compareTo);
92 pattern = String.valueOf(longValue);
93 patternType = PatternType.Integer;
94 } catch (NumberFormatException e)
96 floatValue = Float.valueOf(compareTo);
97 pattern = String.valueOf(floatValue);
98 patternType = PatternType.Float;
104 patternType = PatternType.String;
107 uppercasePattern = pattern == null ? null
108 : pattern.toUpperCase(Locale.ROOT);
110 // if we add regex conditions (e.g. matchesPattern), then
111 // pattern should hold the raw regex, and
112 // regexPattern = Pattern.compile(compareTo);
116 * Constructor for a float-valued numerical match condition. Note that if a
117 * string comparison condition is specified, this will be converted to a
118 * comparison with the float value as string
123 public Matcher(Condition cond, float compareTo)
125 this(cond, String.valueOf(compareTo));
129 * Constructor for an integer-valued numerical match condition. Note that if a
130 * string comparison condition is specified, this will be converted to a
131 * comparison with the integer value as string
136 public Matcher(Condition cond, long compareTo)
138 this(cond, String.valueOf(compareTo));
145 public boolean matches(String compareTo)
147 if (compareTo == null)
149 return matchesNull();
152 boolean matched = false;
156 matched = matchesFloat(compareTo, floatValue);
159 matched = matchesLong(compareTo);
162 matched = matchesString(compareTo);
169 * Executes a non-case-sensitive string comparison to the given value, after
170 * trimming it. Returns true if the test passes, false if it fails.
175 boolean matchesString(String compareTo)
177 boolean matched = false;
178 String upper = compareTo.toUpperCase(Locale.ROOT).trim();
182 matched = upper.equals(uppercasePattern);
185 matched = !upper.equals(uppercasePattern);
188 matched = upper.indexOf(uppercasePattern) > -1;
191 matched = upper.indexOf(uppercasePattern) == -1;
203 * Performs a numerical comparison match condition test against a float value
209 boolean matchesFloat(String testee, float compareTo)
211 if (!condition.isNumeric())
213 // failsafe, shouldn't happen
214 return matches(testee);
220 f = Float.valueOf(testee);
221 } catch (NumberFormatException e)
226 boolean matched = false;
230 matched = f < compareTo;
233 matched = f <= compareTo;
236 matched = f == compareTo;
239 matched = f != compareTo;
242 matched = f > compareTo;
245 matched = f >= compareTo;
255 * A simple hash function that guarantees that when two objects are equal,
256 * they have the same hashcode
259 public int hashCode()
261 return pattern.hashCode() + condition.hashCode() + (int) floatValue;
265 * equals is overridden so that we can safely remove Matcher objects from
266 * collections (e.g. delete an attribute match condition for a feature colour)
269 public boolean equals(Object obj)
271 if (obj == null || !(obj instanceof Matcher))
275 Matcher m = (Matcher) obj;
276 if (condition != m.condition || floatValue != m.floatValue
277 || longValue != m.longValue)
283 return m.pattern == null;
285 return uppercasePattern.equals(m.uppercasePattern);
289 public Condition getCondition()
295 public String getPattern()
301 public String toString()
303 StringBuilder sb = new StringBuilder();
304 sb.append(condition.toString()).append(" ");
305 if (condition.isNumeric())
311 sb.append("'").append(pattern).append("'");
314 return sb.toString();
318 * Performs a numerical comparison match condition test against an integer
324 boolean matchesLong(String compareTo)
326 if (!condition.isNumeric())
328 // failsafe, shouldn't happen
329 return matches(String.valueOf(compareTo));
335 val = Long.valueOf(compareTo);
336 } catch (NumberFormatException e)
339 * try the presented value as a float instead
341 return matchesFloat(compareTo, longValue);
344 boolean matched = false;
348 matched = val < longValue;
351 matched = val <= longValue;
354 matched = val == longValue;
357 matched = val != longValue;
360 matched = val > longValue;
363 matched = val >= longValue;
373 * Tests whether a null value matches the condition. The rule is that any
374 * numeric condition is failed, and only 'negative' string conditions are
375 * matched. So for example <br>
376 * {@code null contains "damaging"}<br>
378 * {@code null does not contain "damaging"}</br>
381 boolean matchesNull()
383 if (condition.isNumeric())
389 return condition == Condition.NotContains
390 || condition == Condition.NotMatches
391 || condition == Condition.NotPresent;