1 package jalview.datamodel.features;
3 import jalview.datamodel.SequenceFeature;
4 import jalview.util.MessageManager;
6 import java.util.ArrayList;
9 public class FeatureMatcherSet implements FeatureMatcherSetI
11 private static final String OR = "OR";
13 private static final String AND = "AND";
15 private static final String SPACE = " ";
17 private static final String CLOSE_BRACKET = ")";
19 private static final String OPEN_BRACKET = "(";
21 private static final String OR_I18N = MessageManager
22 .getString("label.or");
24 private static final String AND_18N = MessageManager
25 .getString("label.and");
27 List<FeatureMatcherI> matchConditions;
29 boolean andConditions;
32 * A factory constructor that converts a stringified object (as output by
33 * toStableString) to an object instance.
37 * <li>(condition1) AND (condition2) AND (condition3)</li>
39 * <li>(condition1) OR (condition2) OR (condition3)</li>
41 * where OR and AND are not case-sensitive, and may not be mixed. Brackets are
42 * optional if there is only one condition.
46 * @see FeatureMatcher#fromString(String)
48 public static FeatureMatcherSet fromString(final String descriptor)
50 String invalid = "Invalid descriptor: " + descriptor;
51 boolean firstCondition = true;
52 FeatureMatcherSet result = new FeatureMatcherSet();
54 String leftToParse = descriptor.trim();
56 while (leftToParse.length() > 0)
59 * inspect AND or OR condition, check not mixed
64 int spacePos = leftToParse.indexOf(SPACE);
67 // trailing junk after a match condition
68 System.err.println(invalid);
71 String conjunction = leftToParse.substring(0, spacePos);
72 leftToParse = leftToParse.substring(spacePos + 1).trim();
73 if (conjunction.equalsIgnoreCase(AND))
77 else if (conjunction.equalsIgnoreCase(OR))
83 // not an AND or an OR - invalid
84 System.err.println(invalid);
90 * now extract the next condition and AND or OR it
92 String nextCondition = leftToParse;
93 if (leftToParse.startsWith(OPEN_BRACKET))
95 int closePos = leftToParse.indexOf(CLOSE_BRACKET);
98 System.err.println(invalid);
101 nextCondition = leftToParse.substring(1, closePos);
102 leftToParse = leftToParse.substring(closePos + 1).trim();
109 FeatureMatcher fm = FeatureMatcher.fromString(nextCondition);
112 System.err.println(invalid);
125 firstCondition = false;
126 } catch (IllegalStateException e)
128 // thrown if OR and AND are mixed
129 System.err.println(invalid);
140 public FeatureMatcherSet()
142 matchConditions = new ArrayList<>();
146 public boolean matches(SequenceFeature feature)
149 * no conditions matches anything
151 if (matchConditions.isEmpty())
161 for (FeatureMatcherI m : matchConditions)
163 if (!m.matches(feature))
174 for (FeatureMatcherI m : matchConditions)
176 if (m.matches(feature))
185 public FeatureMatcherSetI and(FeatureMatcherI m)
187 if (!andConditions && matchConditions.size() > 1)
189 throw new IllegalStateException("Can't add an AND to OR conditions");
191 matchConditions.add(m);
192 andConditions = true;
198 public FeatureMatcherSetI or(FeatureMatcherI m)
200 if (andConditions && matchConditions.size() > 1)
202 throw new IllegalStateException("Can't add an OR to AND conditions");
204 matchConditions.add(m);
205 andConditions = false;
211 public boolean isAnded()
213 return andConditions;
217 public Iterable<FeatureMatcherI> getMatchers()
219 return matchConditions;
223 * Answers a string representation of this object suitable for display, and
224 * possibly internationalized. The format is not guaranteed stable and may
228 public String toString()
230 StringBuilder sb = new StringBuilder();
231 boolean first = true;
232 boolean multiple = matchConditions.size() > 1;
233 for (FeatureMatcherI matcher : matchConditions)
237 String joiner = andConditions ? AND_18N : OR_I18N;
238 sb.append(SPACE).append(joiner.toLowerCase()).append(SPACE);
243 sb.append(OPEN_BRACKET).append(matcher.toString())
244 .append(CLOSE_BRACKET);
248 sb.append(matcher.toString());
251 return sb.toString();
255 public boolean isEmpty()
257 return matchConditions == null || matchConditions.isEmpty();
261 * {@inheritDoc} The output of this method should be parseable by method
262 * <code>fromString<code> to restore the original object.
265 public String toStableString()
267 StringBuilder sb = new StringBuilder();
268 boolean moreThanOne = matchConditions.size() > 1;
269 boolean first = true;
271 for (FeatureMatcherI matcher : matchConditions)
275 String joiner = andConditions ? AND : OR;
276 sb.append(SPACE).append(joiner).append(SPACE);
281 sb.append(OPEN_BRACKET).append(matcher.toStableString())
282 .append(CLOSE_BRACKET);
286 sb.append(matcher.toStableString());
289 return sb.toString();