2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.datamodel.features;
23 import java.util.Locale;
25 import jalview.datamodel.SequenceFeature;
26 import jalview.util.MessageManager;
28 import java.util.ArrayList;
29 import java.util.List;
32 * A class that models one or more match conditions, which may be combined with
33 * AND or OR (but not a mixture)
37 public class FeatureMatcherSet implements FeatureMatcherSetI
39 private static final String OR = "OR";
41 private static final String AND = "AND";
43 private static final String SPACE = " ";
45 private static final String CLOSE_BRACKET = ")";
47 private static final String OPEN_BRACKET = "(";
49 private static final String OR_I18N = MessageManager
50 .getString("label.or");
52 private static final String AND_18N = MessageManager
53 .getString("label.and");
55 List<FeatureMatcherI> matchConditions;
57 boolean andConditions;
60 * A factory constructor that converts a stringified object (as output by
61 * toStableString) to an object instance.
65 * <li>(condition1) AND (condition2) AND (condition3)</li>
67 * <li>(condition1) OR (condition2) OR (condition3)</li>
69 * where OR and AND are not case-sensitive, and may not be mixed. Brackets are
70 * optional if there is only one condition.
74 * @see FeatureMatcher#fromString(String)
76 public static FeatureMatcherSet fromString(final String descriptor)
78 String invalid = "Invalid descriptor: " + descriptor;
79 boolean firstCondition = true;
80 FeatureMatcherSet result = new FeatureMatcherSet();
82 String leftToParse = descriptor.trim();
84 while (leftToParse.length() > 0)
87 * inspect AND or OR condition, check not mixed
92 int spacePos = leftToParse.indexOf(SPACE);
95 // trailing junk after a match condition
96 jalview.bin.Console.errPrintln(invalid);
99 String conjunction = leftToParse.substring(0, spacePos);
100 leftToParse = leftToParse.substring(spacePos + 1).trim();
101 if (conjunction.equalsIgnoreCase(AND))
105 else if (conjunction.equalsIgnoreCase(OR))
111 // not an AND or an OR - invalid
112 jalview.bin.Console.errPrintln(invalid);
118 * now extract the next condition and AND or OR it
120 String nextCondition = leftToParse;
121 if (leftToParse.startsWith(OPEN_BRACKET))
123 int closePos = leftToParse.indexOf(CLOSE_BRACKET);
126 jalview.bin.Console.errPrintln(invalid);
129 nextCondition = leftToParse.substring(1, closePos);
130 leftToParse = leftToParse.substring(closePos + 1).trim();
137 FeatureMatcher fm = FeatureMatcher.fromString(nextCondition);
140 jalview.bin.Console.errPrintln(invalid);
153 firstCondition = false;
154 } catch (IllegalStateException e)
156 // thrown if OR and AND are mixed
157 jalview.bin.Console.errPrintln(invalid);
168 public FeatureMatcherSet()
170 matchConditions = new ArrayList<>();
174 public boolean matches(SequenceFeature feature)
177 * no conditions matches anything
179 if (matchConditions.isEmpty())
189 for (FeatureMatcherI m : matchConditions)
191 if (!m.matches(feature))
202 for (FeatureMatcherI m : matchConditions)
204 if (m.matches(feature))
213 public void and(FeatureMatcherI m)
215 if (!andConditions && matchConditions.size() > 1)
217 throw new IllegalStateException("Can't add an AND to OR conditions");
219 matchConditions.add(m);
220 andConditions = true;
224 public void or(FeatureMatcherI m)
226 if (andConditions && matchConditions.size() > 1)
228 throw new IllegalStateException("Can't add an OR to AND conditions");
230 matchConditions.add(m);
231 andConditions = false;
235 public boolean isAnded()
237 return andConditions;
241 public Iterable<FeatureMatcherI> getMatchers()
243 return matchConditions;
247 * Answers a string representation of this object suitable for display, and
248 * possibly internationalized. The format is not guaranteed stable and may
252 public String toString()
254 StringBuilder sb = new StringBuilder();
255 boolean first = true;
256 boolean multiple = matchConditions.size() > 1;
257 for (FeatureMatcherI matcher : matchConditions)
261 String joiner = andConditions ? AND_18N : OR_I18N;
262 sb.append(SPACE).append(joiner.toLowerCase(Locale.ROOT))
268 sb.append(OPEN_BRACKET).append(matcher.toString())
269 .append(CLOSE_BRACKET);
273 sb.append(matcher.toString());
276 return sb.toString();
280 public boolean isEmpty()
282 return matchConditions == null || matchConditions.isEmpty();
286 * {@inheritDoc} The output of this method should be parseable by method
287 * <code>fromString<code> to restore the original object.
290 public String toStableString()
292 StringBuilder sb = new StringBuilder();
293 boolean moreThanOne = matchConditions.size() > 1;
294 boolean first = true;
296 for (FeatureMatcherI matcher : matchConditions)
300 String joiner = andConditions ? AND : OR;
301 sb.append(SPACE).append(joiner).append(SPACE);
306 sb.append(OPEN_BRACKET).append(matcher.toStableString())
307 .append(CLOSE_BRACKET);
311 sb.append(matcher.toStableString());
314 return sb.toString();