X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2Ffeatures%2FFeatureMatcherSet.java;fp=src%2Fjalview%2Fdatamodel%2Ffeatures%2FFeatureMatcherSet.java;h=b51f2f0a5cc95e3de1f9e3be3404709d1cea6781;hb=b83eff8c672cede0305da3c76823dab414749dde;hp=0000000000000000000000000000000000000000;hpb=d943fcd0dc04fd4974344eddd13902c89fb595b2;p=jalview.git diff --git a/src/jalview/datamodel/features/FeatureMatcherSet.java b/src/jalview/datamodel/features/FeatureMatcherSet.java new file mode 100644 index 0000000..b51f2f0 --- /dev/null +++ b/src/jalview/datamodel/features/FeatureMatcherSet.java @@ -0,0 +1,294 @@ +package jalview.datamodel.features; + +import jalview.datamodel.SequenceFeature; +import jalview.util.MessageManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that models one or more match conditions, which may be combined with + * AND or OR (but not a mixture) + * + * @author gmcarstairs + */ +public class FeatureMatcherSet implements FeatureMatcherSetI +{ + private static final String OR = "OR"; + + private static final String AND = "AND"; + + private static final String SPACE = " "; + + private static final String CLOSE_BRACKET = ")"; + + private static final String OPEN_BRACKET = "("; + + private static final String OR_I18N = MessageManager + .getString("label.or"); + + private static final String AND_18N = MessageManager + .getString("label.and"); + + List matchConditions; + + boolean andConditions; + + /** + * A factory constructor that converts a stringified object (as output by + * toStableString) to an object instance. + * + * Format: + * + * where OR and AND are not case-sensitive, and may not be mixed. Brackets are + * optional if there is only one condition. + * + * @param descriptor + * @return + * @see FeatureMatcher#fromString(String) + */ + public static FeatureMatcherSet fromString(final String descriptor) + { + String invalid = "Invalid descriptor: " + descriptor; + boolean firstCondition = true; + FeatureMatcherSet result = new FeatureMatcherSet(); + + String leftToParse = descriptor.trim(); + + while (leftToParse.length() > 0) + { + /* + * inspect AND or OR condition, check not mixed + */ + boolean and = true; + if (!firstCondition) + { + int spacePos = leftToParse.indexOf(SPACE); + if (spacePos == -1) + { + // trailing junk after a match condition + System.err.println(invalid); + return null; + } + String conjunction = leftToParse.substring(0, spacePos); + leftToParse = leftToParse.substring(spacePos + 1).trim(); + if (conjunction.equalsIgnoreCase(AND)) + { + and = true; + } + else if (conjunction.equalsIgnoreCase(OR)) + { + and = false; + } + else + { + // not an AND or an OR - invalid + System.err.println(invalid); + return null; + } + } + + /* + * now extract the next condition and AND or OR it + */ + String nextCondition = leftToParse; + if (leftToParse.startsWith(OPEN_BRACKET)) + { + int closePos = leftToParse.indexOf(CLOSE_BRACKET); + if (closePos == -1) + { + System.err.println(invalid); + return null; + } + nextCondition = leftToParse.substring(1, closePos); + leftToParse = leftToParse.substring(closePos + 1).trim(); + } + else + { + leftToParse = ""; + } + + FeatureMatcher fm = FeatureMatcher.fromString(nextCondition); + if (fm == null) + { + System.err.println(invalid); + return null; + } + try + { + if (and) + { + result.and(fm); + } + else + { + result.or(fm); + } + firstCondition = false; + } catch (IllegalStateException e) + { + // thrown if OR and AND are mixed + System.err.println(invalid); + return null; + } + + } + return result; + } + + /** + * Constructor + */ + public FeatureMatcherSet() + { + matchConditions = new ArrayList<>(); + } + + @Override + public boolean matches(SequenceFeature feature) + { + /* + * no conditions matches anything + */ + if (matchConditions.isEmpty()) + { + return true; + } + + /* + * AND until failure + */ + if (andConditions) + { + for (FeatureMatcherI m : matchConditions) + { + if (!m.matches(feature)) + { + return false; + } + } + return true; + } + + /* + * OR until match + */ + for (FeatureMatcherI m : matchConditions) + { + if (m.matches(feature)) + { + return true; + } + } + return false; + } + + @Override + public void and(FeatureMatcherI m) + { + if (!andConditions && matchConditions.size() > 1) + { + throw new IllegalStateException("Can't add an AND to OR conditions"); + } + matchConditions.add(m); + andConditions = true; + } + + @Override + public void or(FeatureMatcherI m) + { + if (andConditions && matchConditions.size() > 1) + { + throw new IllegalStateException("Can't add an OR to AND conditions"); + } + matchConditions.add(m); + andConditions = false; + } + + @Override + public boolean isAnded() + { + return andConditions; + } + + @Override + public Iterable getMatchers() + { + return matchConditions; + } + + /** + * Answers a string representation of this object suitable for display, and + * possibly internationalized. The format is not guaranteed stable and may + * change in future. + */ + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + boolean multiple = matchConditions.size() > 1; + for (FeatureMatcherI matcher : matchConditions) + { + if (!first) + { + String joiner = andConditions ? AND_18N : OR_I18N; + sb.append(SPACE).append(joiner.toLowerCase()).append(SPACE); + } + first = false; + if (multiple) + { + sb.append(OPEN_BRACKET).append(matcher.toString()) + .append(CLOSE_BRACKET); + } + else + { + sb.append(matcher.toString()); + } + } + return sb.toString(); + } + + @Override + public boolean isEmpty() + { + return matchConditions == null || matchConditions.isEmpty(); + } + + /** + * {@inheritDoc} The output of this method should be parseable by method + * fromString to restore the original object. + */ + @Override + public String toStableString() + { + StringBuilder sb = new StringBuilder(); + boolean moreThanOne = matchConditions.size() > 1; + boolean first = true; + + for (FeatureMatcherI matcher : matchConditions) + { + if (!first) + { + String joiner = andConditions ? AND : OR; + sb.append(SPACE).append(joiner).append(SPACE); + } + first = false; + if (moreThanOne) + { + sb.append(OPEN_BRACKET).append(matcher.toStableString()) + .append(CLOSE_BRACKET); + } + else + { + sb.append(matcher.toStableString()); + } + } + return sb.toString(); + } + +}