Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 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
294
211
34
0.39
8.7
10
3.4

Classes

Class Line # Actions
FeatureMatcherSet 15 87 34 6
0.9568345595.7%
 

Contributing tests

This file is covered by 15 tests. .

Source view

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