Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  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
314
211
34
0.39
8.7
10
3.4

Classes

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