Clover icon

Coverage Report

  1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package jalview.datamodel.features

File FeatureMatcherSet.java

 

Coverage histogram

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

Code metrics

42
87
10
1
317
213
34
0.39
8.7
10
3.4

Classes

Class Line # Actions
FeatureMatcherSet 37 87 34
0.9568345595.7%
 

Contributing tests

This file is covered by 18 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.datamodel.features;
22   
23    import java.util.Locale;
24   
25    import jalview.datamodel.SequenceFeature;
26    import jalview.util.MessageManager;
27   
28    import java.util.ArrayList;
29    import java.util.List;
30   
31    /**
32    * A class that models one or more match conditions, which may be combined with
33    * AND or OR (but not a mixture)
34    *
35    * @author gmcarstairs
36    */
 
37    public class FeatureMatcherSet implements FeatureMatcherSetI
38    {
39    private static final String OR = "OR";
40   
41    private static final String AND = "AND";
42   
43    private static final String SPACE = " ";
44   
45    private static final String CLOSE_BRACKET = ")";
46   
47    private static final String OPEN_BRACKET = "(";
48   
49    private static final String OR_I18N = MessageManager
50    .getString("label.or");
51   
52    private static final String AND_18N = MessageManager
53    .getString("label.and");
54   
55    List<FeatureMatcherI> matchConditions;
56   
57    boolean andConditions;
58   
59    /**
60    * A factory constructor that converts a stringified object (as output by
61    * toStableString) to an object instance.
62    *
63    * Format:
64    * <ul>
65    * <li>(condition1) AND (condition2) AND (condition3)</li>
66    * <li>or</li>
67    * <li>(condition1) OR (condition2) OR (condition3)</li>
68    * </ul>
69    * where OR and AND are not case-sensitive, and may not be mixed. Brackets are
70    * optional if there is only one condition.
71    *
72    * @param descriptor
73    * @return
74    * @see FeatureMatcher#fromString(String)
75    */
 
76  18 toggle public static FeatureMatcherSet fromString(final String descriptor)
77    {
78  18 String invalid = "Invalid descriptor: " + descriptor;
79  18 boolean firstCondition = true;
80  18 FeatureMatcherSet result = new FeatureMatcherSet();
81   
82  18 String leftToParse = descriptor.trim();
83   
84  44 while (leftToParse.length() > 0)
85    {
86    /*
87    * inspect AND or OR condition, check not mixed
88    */
89  32 boolean and = true;
90  32 if (!firstCondition)
91    {
92  14 int spacePos = leftToParse.indexOf(SPACE);
93  14 if (spacePos == -1)
94    {
95    // trailing junk after a match condition
96  0 jalview.bin.Console.errPrintln(invalid);
97  0 return null;
98    }
99  14 String conjunction = leftToParse.substring(0, spacePos);
100  14 leftToParse = leftToParse.substring(spacePos + 1).trim();
101  14 if (conjunction.equalsIgnoreCase(AND))
102    {
103  5 and = true;
104    }
105  9 else if (conjunction.equalsIgnoreCase(OR))
106    {
107  8 and = false;
108    }
109    else
110    {
111    // not an AND or an OR - invalid
112  1 jalview.bin.Console.errPrintln(invalid);
113  1 return null;
114    }
115    }
116   
117    /*
118    * now extract the next condition and AND or OR it
119    */
120  31 String nextCondition = leftToParse;
121  31 if (leftToParse.startsWith(OPEN_BRACKET))
122    {
123  26 int closePos = leftToParse.indexOf(CLOSE_BRACKET);
124  26 if (closePos == -1)
125    {
126  0 jalview.bin.Console.errPrintln(invalid);
127  0 return null;
128    }
129  26 nextCondition = leftToParse.substring(1, closePos);
130  26 leftToParse = leftToParse.substring(closePos + 1).trim();
131    }
132    else
133    {
134  5 leftToParse = "";
135    }
136   
137  31 FeatureMatcher fm = FeatureMatcher.fromString(nextCondition);
138  31 if (fm == null)
139    {
140  3 jalview.bin.Console.errPrintln(invalid);
141  3 return null;
142    }
143  28 try
144    {
145  28 if (and)
146    {
147  20 result.and(fm);
148    }
149    else
150    {
151  8 result.or(fm);
152    }
153  26 firstCondition = false;
154    } catch (IllegalStateException e)
155    {
156    // thrown if OR and AND are mixed
157  2 jalview.bin.Console.errPrintln(invalid);
158  2 return null;
159    }
160   
161    }
162  12 return result;
163    }
164   
165    /**
166    * Constructor
167    */
 
168  57 toggle public FeatureMatcherSet()
169    {
170  57 matchConditions = new ArrayList<>();
171    }
172   
 
173  47 toggle @Override
174    public boolean matches(SequenceFeature feature)
175    {
176    /*
177    * no conditions matches anything
178    */
179  47 if (matchConditions.isEmpty())
180    {
181  2 return true;
182    }
183   
184    /*
185    * AND until failure
186    */
187  45 if (andConditions)
188    {
189  37 for (FeatureMatcherI m : matchConditions)
190    {
191  39 if (!m.matches(feature))
192    {
193  25 return false;
194    }
195    }
196  12 return true;
197    }
198   
199    /*
200    * OR until match
201    */
202  8 for (FeatureMatcherI m : matchConditions)
203    {
204  14 if (m.matches(feature))
205    {
206  5 return true;
207    }
208    }
209  3 return false;
210    }
211   
 
212  63 toggle @Override
213    public void and(FeatureMatcherI m)
214    {
215  63 if (!andConditions && matchConditions.size() > 1)
216    {
217  4 throw new IllegalStateException("Can't add an AND to OR conditions");
218    }
219  59 matchConditions.add(m);
220  59 andConditions = true;
221    }
222   
 
223  28 toggle @Override
224    public void or(FeatureMatcherI m)
225    {
226  28 if (andConditions && matchConditions.size() > 1)
227    {
228  1 throw new IllegalStateException("Can't add an OR to AND conditions");
229    }
230  27 matchConditions.add(m);
231  27 andConditions = false;
232    }
233   
 
234  6 toggle @Override
235    public boolean isAnded()
236    {
237  6 return andConditions;
238    }
239   
 
240  11 toggle @Override
241    public Iterable<FeatureMatcherI> getMatchers()
242    {
243  11 return matchConditions;
244    }
245   
246    /**
247    * Answers a string representation of this object suitable for display, and
248    * possibly internationalized. The format is not guaranteed stable and may
249    * change in future.
250    */
 
251  6 toggle @Override
252    public String toString()
253    {
254  6 StringBuilder sb = new StringBuilder();
255  6 boolean first = true;
256  6 boolean multiple = matchConditions.size() > 1;
257  6 for (FeatureMatcherI matcher : matchConditions)
258    {
259  6 if (!first)
260    {
261  2 String joiner = andConditions ? AND_18N : OR_I18N;
262  2 sb.append(SPACE).append(joiner.toLowerCase(Locale.ROOT))
263    .append(SPACE);
264    }
265  6 first = false;
266  6 if (multiple)
267    {
268  4 sb.append(OPEN_BRACKET).append(matcher.toString())
269    .append(CLOSE_BRACKET);
270    }
271    else
272    {
273  2 sb.append(matcher.toString());
274    }
275    }
276  6 return sb.toString();
277    }
278   
 
279  34 toggle @Override
280    public boolean isEmpty()
281    {
282  34 return matchConditions == null || matchConditions.isEmpty();
283    }
284   
285    /**
286    * {@inheritDoc} The output of this method should be parseable by method
287    * <code>fromString<code> to restore the original object.
288    */
 
289  24 toggle @Override
290    public String toStableString()
291    {
292  24 StringBuilder sb = new StringBuilder();
293  24 boolean moreThanOne = matchConditions.size() > 1;
294  24 boolean first = true;
295   
296  24 for (FeatureMatcherI matcher : matchConditions)
297    {
298  38 if (!first)
299    {
300  16 String joiner = andConditions ? AND : OR;
301  16 sb.append(SPACE).append(joiner).append(SPACE);
302    }
303  38 first = false;
304  38 if (moreThanOne)
305    {
306  30 sb.append(OPEN_BRACKET).append(matcher.toStableString())
307    .append(CLOSE_BRACKET);
308    }
309    else
310    {
311  8 sb.append(matcher.toStableString());
312    }
313    }
314  24 return sb.toString();
315    }
316   
317    }