1 package jalview.datamodel.features;
3 import jalview.datamodel.SequenceFeature;
4 import jalview.util.MessageManager;
6 import java.util.ArrayList;
10 * A class that models one or more match conditions, which may be combined with
11 * AND or OR (but not a mixture)
15 public class FeatureMatcherSet implements FeatureMatcherSetI
17 private static final String OR = "OR";
19 private static final String AND = "AND";
21 private static final String SPACE = " ";
23 private static final String CLOSE_BRACKET = ")";
25 private static final String OPEN_BRACKET = "(";
27 private static final String OR_I18N = MessageManager
28 .getString("label.or");
30 private static final String AND_18N = MessageManager
31 .getString("label.and");
33 List<FeatureMatcherI> matchConditions;
35 boolean andConditions;
38 * A factory constructor that converts a stringified object (as output by
39 * toStableString) to an object instance.
43 * <li>(condition1) AND (condition2) AND (condition3)</li>
45 * <li>(condition1) OR (condition2) OR (condition3)</li>
47 * where OR and AND are not case-sensitive, and may not be mixed. Brackets are
48 * optional if there is only one condition.
52 * @see FeatureMatcher#fromString(String)
54 public static FeatureMatcherSet fromString(final String descriptor)
56 String invalid = "Invalid descriptor: " + descriptor;
57 boolean firstCondition = true;
58 FeatureMatcherSet result = new FeatureMatcherSet();
60 String leftToParse = descriptor.trim();
62 while (leftToParse.length() > 0)
65 * inspect AND or OR condition, check not mixed
70 int spacePos = leftToParse.indexOf(SPACE);
73 // trailing junk after a match condition
74 System.err.println(invalid);
77 String conjunction = leftToParse.substring(0, spacePos);
78 leftToParse = leftToParse.substring(spacePos + 1).trim();
79 if (conjunction.equalsIgnoreCase(AND))
83 else if (conjunction.equalsIgnoreCase(OR))
89 // not an AND or an OR - invalid
90 System.err.println(invalid);
96 * now extract the next condition and AND or OR it
98 String nextCondition = leftToParse;
99 if (leftToParse.startsWith(OPEN_BRACKET))
101 int closePos = leftToParse.indexOf(CLOSE_BRACKET);
104 System.err.println(invalid);
107 nextCondition = leftToParse.substring(1, closePos);
108 leftToParse = leftToParse.substring(closePos + 1).trim();
115 FeatureMatcher fm = FeatureMatcher.fromString(nextCondition);
118 System.err.println(invalid);
131 firstCondition = false;
132 } catch (IllegalStateException e)
134 // thrown if OR and AND are mixed
135 System.err.println(invalid);
146 public FeatureMatcherSet()
148 matchConditions = new ArrayList<>();
152 public boolean matches(SequenceFeature feature)
155 * no conditions matches anything
157 if (matchConditions.isEmpty())
167 for (FeatureMatcherI m : matchConditions)
169 if (!m.matches(feature))
180 for (FeatureMatcherI m : matchConditions)
182 if (m.matches(feature))
191 public void and(FeatureMatcherI m)
193 if (!andConditions && matchConditions.size() > 1)
195 throw new IllegalStateException("Can't add an AND to OR conditions");
197 matchConditions.add(m);
198 andConditions = true;
202 public void or(FeatureMatcherI m)
204 if (andConditions && matchConditions.size() > 1)
206 throw new IllegalStateException("Can't add an OR to AND conditions");
208 matchConditions.add(m);
209 andConditions = false;
213 public boolean isAnded()
215 return andConditions;
219 public Iterable<FeatureMatcherI> getMatchers()
221 return matchConditions;
225 * Answers a string representation of this object suitable for display, and
226 * possibly internationalized. The format is not guaranteed stable and may
230 public String toString()
232 StringBuilder sb = new StringBuilder();
233 boolean first = true;
234 boolean multiple = matchConditions.size() > 1;
235 for (FeatureMatcherI matcher : matchConditions)
239 String joiner = andConditions ? AND_18N : OR_I18N;
240 sb.append(SPACE).append(joiner.toLowerCase()).append(SPACE);
245 sb.append(OPEN_BRACKET).append(matcher.toString())
246 .append(CLOSE_BRACKET);
250 sb.append(matcher.toString());
253 return sb.toString();
257 public boolean isEmpty()
259 return matchConditions == null || matchConditions.isEmpty();
263 * {@inheritDoc} The output of this method should be parseable by method
264 * <code>fromString<code> to restore the original object.
267 public String toStableString()
269 StringBuilder sb = new StringBuilder();
270 boolean moreThanOne = matchConditions.size() > 1;
271 boolean first = true;
273 for (FeatureMatcherI matcher : matchConditions)
277 String joiner = andConditions ? AND : OR;
278 sb.append(SPACE).append(joiner).append(SPACE);
283 sb.append(OPEN_BRACKET).append(matcher.toStableString())
284 .append(CLOSE_BRACKET);
288 sb.append(matcher.toStableString());
291 return sb.toString();