Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.util.matcher

File Matcher.java

 

Coverage histogram

../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

20
128
13
2
388
235
48
0.38
9.85
6.5
3.69

Classes

Class Line # Actions
Matcher 28 128 48
0.962732996.3%
Matcher.PatternType 30 0 0
-1.0 -
 

Contributing tests

This file is covered by 39 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.util.matcher;
22   
23    import java.util.Objects;
24   
25    /**
26    * A bean to describe one attribute-based filter
27    */
 
28    public class Matcher implements MatcherI
29    {
 
30    public enum PatternType
31    {
32    String, Integer, Float
33    }
34   
35    /*
36    * the comparison condition
37    */
38    private final Condition condition;
39   
40    /*
41    * the string pattern as entered, to compare to
42    */
43    private String pattern;
44   
45    /*
46    * the pattern in upper case, for non-case-sensitive matching
47    */
48    private final String uppercasePattern;
49   
50    /*
51    * the compiled regex if using a pattern match condition
52    * (possible future enhancement)
53    */
54    // private Pattern regexPattern;
55   
56    /*
57    * the value to compare to for a numerical condition with a float pattern
58    */
59    private float floatValue = 0F;
60   
61    /*
62    * the value to compare to for a numerical condition with an integer pattern
63    */
64    private long longValue = 0L;
65   
66    private PatternType patternType;
67   
68    /**
69    * Constructor
70    *
71    * @param cond
72    * @param compareTo
73    * @return
74    * @throws NumberFormatException
75    * if a numerical condition is specified with a non-numeric
76    * comparison value
77    * @throws NullPointerException
78    * if a null condition or comparison string is specified
79    */
 
80  215 toggle public Matcher(Condition cond, String compareTo)
81    {
82  215 Objects.requireNonNull(cond);
83  214 condition = cond;
84   
85  214 if (cond.isNumeric())
86    {
87  121 try
88    {
89  121 longValue = Long.valueOf(compareTo);
90  36 pattern = String.valueOf(longValue);
91  36 patternType = PatternType.Integer;
92    } catch (NumberFormatException e)
93    {
94  85 floatValue = Float.valueOf(compareTo);
95  80 pattern = String.valueOf(floatValue);
96  80 patternType = PatternType.Float;
97    }
98    }
99    else
100    {
101  93 pattern = compareTo;
102  93 patternType = PatternType.String;
103    }
104   
105  209 uppercasePattern = pattern == null ? null : pattern.toUpperCase();
106   
107    // if we add regex conditions (e.g. matchesPattern), then
108    // pattern should hold the raw regex, and
109    // regexPattern = Pattern.compile(compareTo);
110    }
111   
112    /**
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
116    *
117    * @param cond
118    * @param compareTo
119    */
 
120  23 toggle public Matcher(Condition cond, float compareTo)
121    {
122  23 this(cond, String.valueOf(compareTo));
123    }
124   
125    /**
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
129    *
130    * @param cond
131    * @param compareTo
132    */
 
133  14 toggle public Matcher(Condition cond, long compareTo)
134    {
135  14 this(cond, String.valueOf(compareTo));
136    }
137   
138    /**
139    * {@inheritDoc}
140    */
 
141  207 toggle @Override
142    public boolean matches(String compareTo)
143    {
144  207 if (compareTo == null)
145    {
146  27 return matchesNull();
147    }
148   
149  180 boolean matched = false;
150  180 switch (patternType)
151    {
152  49 case Float:
153  49 matched = matchesFloat(compareTo, floatValue);
154  49 break;
155  50 case Integer:
156  50 matched = matchesLong(compareTo);
157  50 break;
158  81 default:
159  81 matched = matchesString(compareTo);
160  81 break;
161    }
162  180 return matched;
163    }
164   
165    /**
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.
168    *
169    * @param compareTo
170    * @return
171    */
 
172  81 toggle boolean matchesString(String compareTo)
173    {
174  81 boolean matched = false;
175  81 String upper = compareTo.toUpperCase().trim();
176  81 switch(condition) {
177  8 case Matches:
178  8 matched = upper.equals(uppercasePattern);
179  8 break;
180  6 case NotMatches:
181  6 matched = !upper.equals(uppercasePattern);
182  6 break;
183  43 case Contains:
184  43 matched = upper.indexOf(uppercasePattern) > -1;
185  43 break;
186  16 case NotContains:
187  16 matched = upper.indexOf(uppercasePattern) == -1;
188  16 break;
189  4 case Present:
190  4 matched = true;
191  4 break;
192  4 default:
193  4 break;
194    }
195  81 return matched;
196    }
197   
198    /**
199    * Performs a numerical comparison match condition test against a float value
200    *
201    * @param testee
202    * @param compareTo
203    * @return
204    */
 
205  77 toggle boolean matchesFloat(String testee, float compareTo)
206    {
207  77 if (!condition.isNumeric())
208    {
209    // failsafe, shouldn't happen
210  4 return matches(testee);
211    }
212   
213  73 float f = 0f;
214  73 try
215    {
216  73 f = Float.valueOf(testee);
217    } catch (NumberFormatException e)
218    {
219  30 return false;
220    }
221   
222  43 boolean matched = false;
223  43 switch (condition) {
224  10 case LT:
225  10 matched = f < compareTo;
226  10 break;
227  7 case LE:
228  7 matched = f <= compareTo;
229  7 break;
230  8 case EQ:
231  8 matched = f == compareTo;
232  8 break;
233  4 case NE:
234  4 matched = f != compareTo;
235  4 break;
236  8 case GT:
237  8 matched = f > compareTo;
238  8 break;
239  6 case GE:
240  6 matched = f >= compareTo;
241  6 break;
242  0 default:
243  0 break;
244    }
245   
246  43 return matched;
247    }
248   
249    /**
250    * A simple hash function that guarantees that when two objects are equal,
251    * they have the same hashcode
252    */
 
253  8 toggle @Override
254    public int hashCode()
255    {
256  8 return pattern.hashCode() + condition.hashCode() + (int) floatValue;
257    }
258   
259    /**
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)
262    */
 
263  26 toggle @Override
264    public boolean equals(Object obj)
265    {
266  26 if (obj == null || !(obj instanceof Matcher))
267    {
268  6 return false;
269    }
270  20 Matcher m = (Matcher) obj;
271  20 if (condition != m.condition || floatValue != m.floatValue
272    || longValue != m.longValue)
273    {
274  9 return false;
275    }
276  11 if (pattern == null)
277    {
278  0 return m.pattern == null;
279    }
280  11 return uppercasePattern.equals(m.uppercasePattern);
281    }
282   
 
283  89 toggle @Override
284    public Condition getCondition()
285    {
286  89 return condition;
287    }
288   
 
289  88 toggle @Override
290    public String getPattern()
291    {
292  88 return pattern;
293    }
294   
 
295  4 toggle @Override
296    public String toString()
297    {
298  4 StringBuilder sb = new StringBuilder();
299  4 sb.append(condition.toString()).append(" ");
300  4 if (condition.isNumeric())
301    {
302  2 sb.append(pattern);
303    }
304    else
305    {
306  2 sb.append("'").append(pattern).append("'");
307    }
308   
309  4 return sb.toString();
310    }
311   
312    /**
313    * Performs a numerical comparison match condition test against an integer
314    * value
315    *
316    * @param compareTo
317    * @return
318    */
 
319  54 toggle boolean matchesLong(String compareTo)
320    {
321  54 if (!condition.isNumeric())
322    {
323    // failsafe, shouldn't happen
324  4 return matches(String.valueOf(compareTo));
325    }
326   
327  50 long val = 0L;
328  50 try
329    {
330  50 val = Long.valueOf(compareTo);
331    } catch (NumberFormatException e)
332    {
333    /*
334    * try the presented value as a float instead
335    */
336  24 return matchesFloat(compareTo, longValue);
337    }
338   
339  26 boolean matched = false;
340  26 switch (condition) {
341  3 case LT:
342  3 matched = val < longValue;
343  3 break;
344  3 case LE:
345  3 matched = val <= longValue;
346  3 break;
347  3 case EQ:
348  3 matched = val == longValue;
349  3 break;
350  2 case NE:
351  2 matched = val != longValue;
352  2 break;
353  3 case GT:
354  3 matched = val > longValue;
355  3 break;
356  12 case GE:
357  12 matched = val >= longValue;
358  12 break;
359  0 default:
360  0 break;
361    }
362   
363  26 return matched;
364    }
365   
366    /**
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>
371    * fails, but <br>
372    * {@code null does not contain "damaging"}</br>
373    * passes.
374    */
 
375  27 toggle boolean matchesNull()
376    {
377  27 if (condition.isNumeric())
378    {
379  16 return false;
380    }
381    else
382    {
383  11 return condition == Condition.NotContains
384    || condition == Condition.NotMatches
385    || condition == Condition.NotPresent;
386    }
387    }
388    }