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.Objects;
26 * A bean to describe one attribute-based filter
28 public class Matcher implements MatcherI
30 public enum PatternType
32 String, Integer, Float
36 * the comparison condition
38 private final Condition condition;
41 * the string pattern as entered, to compare to
43 private String pattern;
46 * the pattern in upper case, for non-case-sensitive matching
48 private final String uppercasePattern;
51 * the compiled regex if using a pattern match condition
52 * (possible future enhancement)
54 // private Pattern regexPattern;
57 * the value to compare to for a numerical condition with a float pattern
59 private float floatValue = 0F;
62 * the value to compare to for a numerical condition with an integer pattern
64 private long longValue = 0L;
66 private PatternType patternType;
74 * @throws NumberFormatException
75 * if a numerical condition is specified with a non-numeric
77 * @throws NullPointerException
78 * if a null condition or comparison string is specified
80 public Matcher(Condition cond, String compareTo)
82 Objects.requireNonNull(cond);
89 longValue = Long.valueOf(compareTo);
90 pattern = String.valueOf(longValue);
91 patternType = PatternType.Integer;
92 } catch (NumberFormatException e)
94 floatValue = Float.valueOf(compareTo);
95 pattern = String.valueOf(floatValue);
96 patternType = PatternType.Float;
102 patternType = PatternType.String;
105 uppercasePattern = pattern == null ? null : pattern.toUpperCase();
107 // if we add regex conditions (e.g. matchesPattern), then
108 // pattern should hold the raw regex, and
109 // regexPattern = Pattern.compile(compareTo);
113 * Constructor for a float-valued numerical match condition. Note that if a
114 * string comparison condition is specified, this will be converted to a
115 * comparison with the float value as string
120 public Matcher(Condition cond, float compareTo)
122 this(cond, String.valueOf(compareTo));
126 * Constructor for an integer-valued numerical match condition. Note that if a
127 * string comparison condition is specified, this will be converted to a
128 * comparison with the integer value as string
133 public Matcher(Condition cond, long compareTo)
135 this(cond, String.valueOf(compareTo));
142 public boolean matches(String compareTo)
144 if (compareTo == null)
146 return matchesNull();
149 boolean matched = false;
153 matched = matchesFloat(compareTo, floatValue);
156 matched = matchesLong(compareTo);
159 matched = matchesString(compareTo);
166 * Executes a non-case-sensitive string comparison to the given value, after
167 * trimming it. Returns true if the test passes, false if it fails.
172 boolean matchesString(String compareTo)
174 boolean matched = false;
175 String upper = compareTo.toUpperCase().trim();
178 matched = upper.equals(uppercasePattern);
181 matched = !upper.equals(uppercasePattern);
184 matched = upper.indexOf(uppercasePattern) > -1;
187 matched = upper.indexOf(uppercasePattern) == -1;
199 * Performs a numerical comparison match condition test against a float value
205 boolean matchesFloat(String testee, float compareTo)
207 if (!condition.isNumeric())
209 // failsafe, shouldn't happen
210 return matches(testee);
216 f = Float.valueOf(testee);
217 } catch (NumberFormatException e)
222 boolean matched = false;
225 matched = f < compareTo;
228 matched = f <= compareTo;
231 matched = f == compareTo;
234 matched = f != compareTo;
237 matched = f > compareTo;
240 matched = f >= compareTo;
250 * A simple hash function that guarantees that when two objects are equal,
251 * they have the same hashcode
254 public int hashCode()
256 return pattern.hashCode() + condition.hashCode() + (int) floatValue;
260 * equals is overridden so that we can safely remove Matcher objects from
261 * collections (e.g. delete an attribute match condition for a feature colour)
264 public boolean equals(Object obj)
266 if (obj == null || !(obj instanceof Matcher))
270 Matcher m = (Matcher) obj;
271 if (condition != m.condition || floatValue != m.floatValue
272 || longValue != m.longValue)
278 return m.pattern == null;
280 return uppercasePattern.equals(m.uppercasePattern);
284 public Condition getCondition()
290 public String getPattern()
296 public String toString()
298 StringBuilder sb = new StringBuilder();
299 sb.append(condition.toString()).append(" ");
300 if (condition.isNumeric())
306 sb.append("'").append(pattern).append("'");
309 return sb.toString();
313 * Performs a numerical comparison match condition test against an integer
319 boolean matchesLong(String compareTo)
321 if (!condition.isNumeric())
323 // failsafe, shouldn't happen
324 return matches(String.valueOf(compareTo));
330 val = Long.valueOf(compareTo);
331 } catch (NumberFormatException e)
334 * try the presented value as a float instead
336 return matchesFloat(compareTo, longValue);
339 boolean matched = false;
342 matched = val < longValue;
345 matched = val <= longValue;
348 matched = val == longValue;
351 matched = val != longValue;
354 matched = val > longValue;
357 matched = val >= longValue;
367 * Tests whether a null value matches the condition. The rule is that any
368 * numeric condition is failed, and only 'negative' string conditions are
369 * matched. So for example <br>
370 * {@code null contains "damaging"}<br>
372 * {@code null does not contain "damaging"}</br>
375 boolean matchesNull()
377 if (condition.isNumeric())
383 return condition == Condition.NotContains
384 || condition == Condition.NotMatches
385 || condition == Condition.NotPresent;