label.matchCondition_notcontains = Does not contain
label.matchCondition_matches = Matches
label.matchCondition_notmatches = Does not match
-label.matchCondition_eq = Is equal to
-label.matchCondition_ne = Is not equal to
-label.matchCondition_lt = Is less than
-label.matchCondition_le = Is less than or equal to
-label.matchCondition_gt = Is greater than
-label.matchCondition_ge = Is greater than or equal to
+label.matchCondition_eq = =
+label.matchCondition_ne = not =
+label.matchCondition_lt = <
+label.matchCondition_le = <=
+label.matchCondition_gt = >
+label.matchCondition_ge = >=
package jalview.api;
import jalview.datamodel.SequenceFeature;
-import jalview.util.matcher.KeyedMatcherI;
+import jalview.util.matcher.KeyedMatcherSetI;
import java.awt.Color;
*
* @param filter
*/
- public void setAttributeFilters(KeyedMatcherI filter);
+ public void setAttributeFilters(KeyedMatcherSetI filter);
/**
* Answers the attribute value filters for the colour scheme, or null if no
*
* @return
*/
- public KeyedMatcherI getAttributeFilters();
+ public KeyedMatcherSetI getAttributeFilters();
}
import jalview.util.matcher.Condition;
import jalview.util.matcher.KeyedMatcher;
import jalview.util.matcher.KeyedMatcherI;
-import jalview.util.matcher.Matcher;
-import jalview.util.matcher.MatcherI;
+import jalview.util.matcher.KeyedMatcherSet;
+import jalview.util.matcher.KeyedMatcherSetI;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;
/**
* Populates the attribute filter fields for the initial display
*
- * @param attributeFilters
+ * @param filters
*/
- void setInitialFilters(KeyedMatcherI attributeFilters)
+ void setInitialFilters(KeyedMatcherSetI filters)
{
// todo generalise to populate N conditions
-
- if (attributeFilters != null)
- {
- filterAttribute.setSelectedItem(attributeFilters.getKey());
- filterCondition.setSelectedItem(attributeFilters.getMatcher()
- .getCondition());
- filterValue.setText(attributeFilters.getMatcher().getPattern());
-
- KeyedMatcherI second = attributeFilters.getSecondMatcher();
- if (second != null)
- {
- // todo add OR/AND condition to gui
- filterAttribute2.setSelectedItem(second.getKey());
- filterCondition2
- .setSelectedItem(second.getMatcher().getCondition());
- filterValue2.setText(second.getMatcher().getPattern());
- }
+
+ if (filters == null)
+ {
+ return;
+ }
+
+ Iterator<KeyedMatcherI> theFilters = filters.getMatchers();
+ if (theFilters.hasNext())
+ {
+ KeyedMatcherI filter = theFilters.next();
+ filterAttribute.setSelectedItem(filter.getKey());
+ filterCondition.setSelectedItem(filter.getMatcher().getCondition());
+ filterValue.setText(filter.getMatcher().getPattern());
+ }
+ if (theFilters.hasNext())
+ {
+ KeyedMatcherI filter = theFilters.next();
+ boolean anded = filters.isAnded();
+ // todo add OR/AND condition to gui
+ // - user choice for the second condition, fixed thereafter
+ filterAttribute2.setSelectedItem(filter.getKey());
+ filterCondition2.setSelectedItem(filter.getMatcher().getCondition());
+ filterValue2.setText(filter.getMatcher().getPattern());
}
}
String attribute = (String) filterAttribute.getSelectedItem();
Condition cond = (Condition) filterCondition.getSelectedItem();
String pattern = filterValue.getText().trim();
- if (pattern.length() > 1)
+ if (pattern.length() > 0)
{
- MatcherI filter = new Matcher(cond, pattern);
- KeyedMatcherI km = new KeyedMatcher(attribute, filter);
+ KeyedMatcherSetI filters = new KeyedMatcherSet();
+ KeyedMatcherI km = new KeyedMatcher(attribute, cond, pattern);
+ filters.and(km);
/*
* is there a second condition?
- * todo: generalise to N conditions
+ * todo: allow N conditions with choice of AND or OR (but not both!)
*/
pattern = filterValue2.getText().trim();
if (pattern.length() > 1)
{
attribute = (String) filterAttribute2.getSelectedItem();
cond = (Condition) filterCondition2.getSelectedItem();
- filter = new Matcher(cond, pattern);
- km = km.and(attribute, filter);
+ KeyedMatcherI km2 = new KeyedMatcher(attribute, cond, pattern);
+ filters.and(km2);
}
- acg.setAttributeFilters(km);
+ acg.setAttributeFilters(filters);
}
}
filterAttribute.addItem(attName);
filterAttribute2.addItem(attName);
}
- filterAttribute.addActionListener(new ActionListener()
+ filterAttribute.addItemListener(new ItemListener()
{
@Override
- public void actionPerformed(ActionEvent e)
+ public void itemStateChanged(ItemEvent e)
{
changeColour(true);
}
{
filterCondition.addItem(cond);
}
- filterCondition.addActionListener(new ActionListener()
+ filterCondition.addItemListener(new ItemListener()
{
@Override
- public void actionPerformed(ActionEvent e)
+ public void itemStateChanged(ItemEvent e)
{
changeColour(true);
}
import jalview.datamodel.SequenceFeature;
import jalview.util.ColorUtils;
import jalview.util.Format;
-import jalview.util.matcher.KeyedMatcherI;
+import jalview.util.matcher.KeyedMatcherSetI;
import java.awt.Color;
import java.util.StringTokenizer;
final private float deltaBlue;
/*
- * optional filter by attribute values
+ * optional filter(s) by attribute values
*/
- private KeyedMatcherI attributeFilters;
+ private KeyedMatcherSetI attributeFilters;
/**
* Parses a Jalview features file format colour descriptor
}
/**
- * Answers true if there are any attribute value filters defined, and the
- * feature matches all of the filter conditions
+ * Answers true if either there are no attribute value filters defined, or the
+ * feature matches all of the filter conditions. Answers false if the feature
+ * fails the filter conditions.
*
* @param feature
*
*/
boolean matchesFilters(SequenceFeature feature)
{
+ if (attributeFilters == null)
+ {
+ return true;
+ }
+
Function<String, String> valueProvider = key -> feature.otherDetails == null ? null
: (feature.otherDetails.containsKey(key) ? feature.otherDetails
.get(key).toString() : null);
- return attributeFilters == null ? true : attributeFilters
- .matches(valueProvider);
+ return attributeFilters.matches(valueProvider);
}
/**
* @param filter
*/
@Override
- public void setAttributeFilters(KeyedMatcherI matcher)
+ public void setAttributeFilters(KeyedMatcherSetI matcher)
{
attributeFilters = matcher;
}
@Override
- public KeyedMatcherI getAttributeFilters()
+ public KeyedMatcherSetI getAttributeFilters()
{
return attributeFilters;
}
import java.util.function.Function;
+/**
+ * An immutable class that models one or more match conditions, each of which is
+ * applied to the value obtained by lookup given the match key.
+ * <p>
+ * For example, the value provider could be a SequenceFeature's attributes map,
+ * and the conditions might be
+ * <ul>
+ * <li>CSQ contains "pathological"</li>
+ * <li>AND</li>
+ * <li>AF <= 1.0e-5</li>
+ * </ul>
+ *
+ * @author gmcarstairs
+ *
+ */
public class KeyedMatcher implements KeyedMatcherI
{
- private String key;
+ final private String key;
- private MatcherI matcher;
+ final private MatcherI matcher;
- /*
- * an optional second condition
- */
- KeyedMatcherI combineWith;
-
- /*
- * if true, any second condition is AND-ed with this one
- * if false,any second condition is OR-ed with this one
+ /**
+ * Constructor given a key, a test condition and a match pattern
+ *
+ * @param theKey
+ * @param cond
+ * @param pattern
*/
- boolean combineAnd;
+ public KeyedMatcher(String theKey, Condition cond, String pattern)
+ {
+ key = theKey;
+ matcher = new Matcher(cond, pattern);
+ }
/**
- * Constructor given a match condition
+ * Constructor given a key, a test condition and a numerical value to compare
+ * to. Note that if a non-numerical condition is specified, the float will be
+ * converted to a string.
*
- * @param m
+ * @param theKey
+ * @param cond
+ * @param value
*/
- public KeyedMatcher(String theKey, MatcherI m)
+ public KeyedMatcher(String theKey, Condition cond, float value)
{
key = theKey;
- matcher = m;
+ matcher = new Matcher(cond, value);
}
@Override
public boolean matches(Function<String, String> valueProvider)
{
String value = valueProvider.apply(key);
- boolean matched = matcher.matches(value);
-
- /*
- * apply a second condition if there is one, using
- * lazy evalution of AND and OR combinations
- */
- if (combineWith != null)
- {
- if (combineAnd && matched)
- {
- matched = combineWith.matches(valueProvider);
- }
- if (!combineAnd && !matched)
- {
- matched = combineWith.matches(valueProvider);
- }
- }
-
- return matched;
- }
-
- @Override
- public KeyedMatcherI and(String key2, MatcherI m)
- {
- return combineWith(key2, m, true);
- }
-
- @Override
- public KeyedMatcherI or(String key2, MatcherI m)
- {
- return combineWith(key2, m, false);
- }
-
- /**
- * Answers a Matcher that is the logical combination of this one with the
- * given argument. The two matchers are AND-ed if and is true, else OR-ed.
- *
- * @param key2
- * @param condition2
- * @param and
- * @return
- */
- KeyedMatcher combineWith(String key2, MatcherI condition2,
- boolean and)
- {
- if (condition2 == null)
- {
- return this;
- }
-
- KeyedMatcher combined = new KeyedMatcher(key2, condition2);
- combined.combineWith = this;
- combined.combineAnd = and;
-
- return combined;
+ return matcher.matches(value);
}
@Override
return matcher;
}
- @Override
- public KeyedMatcherI getSecondMatcher()
- {
- return combineWith;
- }
-
- @Override
- public boolean isAnded()
- {
- return combineAnd;
- }
-
/**
* Answers a string description of this matcher, suitable for debugging or
* logging. The format may change in future.
sb.append(key).append(" ").append(matcher.getCondition().name())
.append(" ").append(matcher.getPattern());
- if (combineWith != null)
- {
- sb.append(" ").append(combineAnd ? "AND (" : "OR (")
- .append(combineWith.toString()).append(")");
- }
return sb.toString();
}
}
import java.util.function.Function;
+/**
+ * An interface for an object that can apply one or more match conditions, given
+ * a key-value provider. The match conditions are stored against key values, and
+ * applied to the value obtained by a key-value lookup.
+ *
+ * @author gmcarstairs
+ */
public interface KeyedMatcherI
{
- boolean matches(Function<String, String> valueProvider);
-
/**
- * Answers a new object that matches the logical AND of this and m
+ * Answers true if the value provided for this matcher's key passes this
+ * matcher's match condition
*
- * @param m
+ * @param valueProvider
* @return
*/
- KeyedMatcherI and(String key, MatcherI m);
+ boolean matches(Function<String, String> valueProvider);
/**
- * Answers a new object that matches the logical OR of this and m
+ * Answers the value key this matcher operates on
*
- * @param m
* @return
*/
- KeyedMatcherI or(String key, MatcherI m);
-
String getKey();
+ /**
+ * Answers the match condition that is applied
+ *
+ * @return
+ */
MatcherI getMatcher();
-
- KeyedMatcherI getSecondMatcher();
-
- boolean isAnded();
}
--- /dev/null
+package jalview.util.matcher;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+public class KeyedMatcherSet implements KeyedMatcherSetI
+{
+ List<KeyedMatcherI> matchConditions;
+
+ boolean andConditions;
+
+ /**
+ * Constructor
+ */
+ public KeyedMatcherSet()
+ {
+ matchConditions = new ArrayList<>();
+ }
+
+ @Override
+ public boolean matches(Function<String, String> valueProvider)
+ {
+ /*
+ * no conditions matches anything
+ */
+ if (matchConditions.isEmpty())
+ {
+ return true;
+ }
+
+ /*
+ * AND until failure
+ */
+ if (andConditions)
+ {
+ for (KeyedMatcherI m : matchConditions)
+ {
+ if (!m.matches(valueProvider))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * OR until match
+ */
+ for (KeyedMatcherI m : matchConditions)
+ {
+ if (m.matches(valueProvider))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public KeyedMatcherSetI and(KeyedMatcherI m)
+ {
+ if (!andConditions && matchConditions.size() > 1)
+ {
+ throw new IllegalStateException("Can't add an AND to OR conditions");
+ }
+ matchConditions.add(m);
+ andConditions = true;
+
+ return this;
+ }
+
+ @Override
+ public KeyedMatcherSetI or(KeyedMatcherI m)
+ {
+ if (andConditions && matchConditions.size() > 1)
+ {
+ throw new IllegalStateException("Can't add an OR to AND conditions");
+ }
+ matchConditions.add(m);
+ andConditions = false;
+
+ return this;
+ }
+
+ @Override
+ public boolean isAnded()
+ {
+ return andConditions;
+ }
+
+ @Override
+ public Iterator<KeyedMatcherI> getMatchers()
+ {
+ return matchConditions.iterator();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (KeyedMatcherI matcher : matchConditions)
+ {
+ if (!first)
+ {
+ sb.append(andConditions ? " AND " : " OR ");
+ }
+ first = false;
+ sb.append("(").append(matcher.toString()).append(")");
+ }
+ return sb.toString();
+ }
+
+}
--- /dev/null
+package jalview.util.matcher;
+
+import java.util.Iterator;
+import java.util.function.Function;
+
+/**
+ * An interface to describe a set of one or more key-value match conditions,
+ * where all conditions are combined with either AND or OR
+ *
+ * @author gmcarstairs
+ *
+ */
+public interface KeyedMatcherSetI
+{
+ /**
+ * Answers true if the value provided for this matcher's key passes this
+ * matcher's match condition
+ *
+ * @param valueProvider
+ * @return
+ */
+ boolean matches(Function<String, String> valueProvider);
+
+ /**
+ * Answers a new object that matches the logical AND of this and m
+ *
+ * @param m
+ * @return
+ * @throws IllegalStateException
+ * if an attempt is made to AND to existing OR-ed conditions
+ */
+ KeyedMatcherSetI and(KeyedMatcherI m);
+
+ /**
+ * Answers true if any second condition is AND-ed with this one, false if it
+ * is OR-ed
+ *
+ * @return
+ */
+ boolean isAnded();
+
+ /**
+ * Answers a new object that matches the logical OR of this and m
+ *
+ * @param m
+ * @return
+ * @throws IllegalStateException
+ * if an attempt is made to OR to existing AND-ed conditions
+ */
+ KeyedMatcherSetI or(KeyedMatcherI m);
+
+ /**
+ * Answers an iterator over the combined match conditions
+ *
+ * @return
+ */
+ Iterator<KeyedMatcherI> getMatchers();
+}
package jalview.util.matcher;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
if (cond.isNumeric())
{
value = Float.valueOf(compareTo);
+ pattern = String.valueOf(value);
}
- // pattern matches will be non-case-sensitive
- pattern = compareTo.toUpperCase();
+ else
+ {
+ // pattern matches will be non-case-sensitive
+ pattern = compareTo.toUpperCase();
+ }
+
// if we add regex conditions (e.g. matchesPattern), then
// pattern should hold the raw regex, and
// regexPattern = Pattern.compile(compareTo);
*/
public Matcher(Condition cond, float compareTo)
{
+ Objects.requireNonNull(cond);
condition = cond;
value = compareTo;
- pattern = String.valueOf(compareTo);
+ pattern = String.valueOf(compareTo).toUpperCase();
}
/**
return false;
}
Matcher m = (Matcher) obj;
- return condition != m.condition || value != m.value
- || !pattern.equals(m.pattern);
+ return condition == m.condition && value == m.value
+ && pattern.equals(m.pattern);
}
@Override
{
return value;
}
+
+ @Override
+ public String toString()
+ {
+ return condition.name() + " " + pattern;
+ }
}
--- /dev/null
+package jalview.util.matcher;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Locale;
+
+import org.testng.annotations.Test;
+
+public class ConditionTest
+{
+ @Test
+ public void testToString()
+ {
+ Locale.setDefault(Locale.UK);
+ assertEquals(Condition.Contains.toString(), "Contains");
+ assertEquals(Condition.NotContains.toString(), "Does not contain");
+ assertEquals(Condition.Matches.toString(), "Matches");
+ assertEquals(Condition.NotMatches.toString(), "Does not match");
+ assertEquals(Condition.LT.toString(), "Is less than");
+ assertEquals(Condition.LE.toString(), "Is less than or equal to");
+ assertEquals(Condition.GT.toString(), "Is greater than");
+ assertEquals(Condition.GE.toString(), "Is greater than or equal to");
+ assertEquals(Condition.EQ.toString(), "Is equal to");
+ assertEquals(Condition.NE.toString(), "Is not equal to");
+
+ /*
+ * repeat call to get coverage of cached value
+ */
+ assertEquals(Condition.NE.toString(), "Is not equal to");
+ }
+}
--- /dev/null
+package jalview.util.matcher;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.function.Function;
+
+import org.testng.annotations.Test;
+
+public class KeyedMatcherSetTest
+{
+ @Test
+ public void testMatches()
+ {
+ /*
+ * a numeric matcher - MatcherTest covers more conditions
+ */
+ KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F);
+ assertTrue(km.matches(key -> "-2"));
+ assertTrue(km.matches(key -> "-1"));
+ assertFalse(km.matches(key -> "-3"));
+ assertFalse(km.matches(key -> ""));
+ assertFalse(km.matches(key -> "junk"));
+ assertFalse(km.matches(key -> null));
+
+ /*
+ * a string pattern matcher
+ */
+ km = new KeyedMatcher("AF", Condition.Contains, "Cat");
+ assertTrue(km.matches(key -> "AF".equals(key) ? "raining cats and dogs"
+ : "showers"));
+ }
+
+ @Test
+ public void testAnd()
+ {
+ // condition1: AF value contains "dog" (matches)
+ KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.Contains, "dog");
+ // condition 2: CSQ value does not contain "how" (does not match)
+ KeyedMatcherI km2 = new KeyedMatcher("CSQ", Condition.NotContains,
+ "how");
+
+ Function<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
+ : "showers";
+ assertTrue(km1.matches(vp));
+ assertFalse(km2.matches(vp));
+
+ KeyedMatcherSetI kms = new KeyedMatcherSet();
+ assertTrue(kms.matches(vp)); // if no conditions, then 'all' pass
+ kms.and(km1);
+ assertTrue(kms.matches(vp));
+ kms.and(km2);
+ assertFalse(kms.matches(vp));
+ }
+
+ @Test
+ public void testToString()
+ {
+ KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.LT, 1.2f);
+ assertEquals(km1.toString(), "AF LT 1.2");
+
+ KeyedMatcher km2 = new KeyedMatcher("CLIN_SIG", Condition.NotContains, "path");
+ assertEquals(km2.toString(), "CLIN_SIG NotContains PATH");
+
+ /*
+ * AND them
+ */
+ KeyedMatcherSetI kms = new KeyedMatcherSet();
+ assertEquals(kms.toString(), "");
+ kms.and(km1);
+ assertEquals(kms.toString(), "(AF LT 1.2)");
+ kms.and(km2);
+ assertEquals(kms.toString(),
+ "(AF LT 1.2) AND (CLIN_SIG NotContains PATH)");
+
+ /*
+ * OR them
+ */
+ kms = new KeyedMatcherSet();
+ assertEquals(kms.toString(), "");
+ kms.or(km1);
+ assertEquals(kms.toString(), "(AF LT 1.2)");
+ kms.or(km2);
+ assertEquals(kms.toString(),
+ "(AF LT 1.2) OR (CLIN_SIG NotContains PATH)");
+ }
+
+ /**
+ * @return
+ */
+ protected KeyedMatcher km3()
+ {
+ return new KeyedMatcher("CSQ", Condition.Contains, "benign");
+ }
+
+ @Test
+ public void testOr()
+ {
+ // condition1: AF value contains "dog" (matches)
+ KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.Contains, "dog");
+ // condition 2: CSQ value does not contain "how" (does not match)
+ KeyedMatcherI km2 = new KeyedMatcher("CSQ", Condition.NotContains,
+ "how");
+
+ Function<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
+ : "showers";
+ assertTrue(km1.matches(vp));
+ assertFalse(km2.matches(vp));
+
+ KeyedMatcherSetI kms = new KeyedMatcherSet();
+ kms.or(km2);
+ assertFalse(kms.matches(vp));
+ kms.or(km1);
+ assertTrue(kms.matches(vp));
+ }
+}
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
-import java.util.function.Function;
-
import org.testng.annotations.Test;
public class KeyedMatcherTest
/*
* a numeric matcher - MatcherTest covers more conditions
*/
- MatcherI m1 = new Matcher(Condition.GE, -2f);
- KeyedMatcherI km = new KeyedMatcher("AF", m1);
+ KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F);
assertTrue(km.matches(key -> "-2"));
assertTrue(km.matches(key -> "-1"));
assertFalse(km.matches(key -> "-3"));
/*
* a string pattern matcher
*/
- MatcherI m2 = new Matcher(Condition.Contains, "Cat");
- km = new KeyedMatcher("AF", m2);
+ km = new KeyedMatcher("AF", Condition.Contains, "Cat");
assertTrue(km.matches(key -> "AF".equals(key) ? "raining cats and dogs"
: "showers"));
}
@Test
- public void testAnd()
+ public void testToString()
{
- // condition1: AF value contains "dog" (matches)
- KeyedMatcherI km1 = new KeyedMatcher("AF", new Matcher(
- Condition.Contains, "dog"));
-
- Function<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
- : "showers";
- assertTrue(km1.matches(vp));
-
- // condition 2: CSQ value does not contain "how" (does not match)
- KeyedMatcherI km2 = km1.and("CSQ", new Matcher(Condition.NotContains,
- "how"));
- assertFalse(km2.matches(vp));
+ KeyedMatcherI km = new KeyedMatcher("AF", Condition.LT, 1.2f);
+ assertEquals(km.toString(), "AF LT 1.2");
}
@Test
- public void testToString()
+ public void testGetKey()
{
- KeyedMatcherI km = new KeyedMatcher("AF",
- new Matcher(Condition.LT, 1.2f));
- assertEquals(km.toString(), "AF LT 1.2");
-
- /*
- * add an AND condition
- */
- km = km.and("CLIN_SIG", new Matcher(Condition.NotContains, "path"));
- assertEquals(km.toString(), "CLIN_SIG NotContains PATH AND (AF LT 1.2)");
-
- /*
- * add an OR condition
- */
- km = km.or("CSQ", new Matcher(Condition.Contains, "benign"));
- assertEquals(km.toString(),
- "CSQ Contains BENIGN OR (CLIN_SIG NotContains PATH AND (AF LT 1.2))");
+ KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F);
+ assertEquals(km.getKey(), "AF");
}
@Test
- public void testOr()
+ public void testGetMatcher()
{
- // condition1: AF value contains "dog" (matches)
- KeyedMatcherI km1 = new KeyedMatcher("AF", new Matcher(
- Condition.Contains, "dog"));
-
- Function<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
- : "showers";
- assertTrue(km1.matches(vp));
-
- // condition 2: CSQ value does not contain "how" (does not match)
- // the OR combination still passes
- KeyedMatcherI km2 = km1.or("CSQ", new Matcher(Condition.NotContains,
- "how"));
- assertTrue(km2.matches(vp));
+ KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F);
+ assertEquals(km.getMatcher().getCondition(), Condition.GE);
+ assertEquals(km.getMatcher().getFloatValue(), -2F);
+ assertEquals(km.getMatcher().getPattern(), "-2.0");
}
}
package jalview.util.matcher;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
import org.testng.annotations.Test;
public class MatcherTest
{
+ @Test
+ public void testConstructor()
+ {
+ MatcherI m = new Matcher(Condition.Contains, "foo");
+ assertEquals(m.getCondition(), Condition.Contains);
+ assertEquals(m.getPattern(), "FOO"); // all comparisons upper-cased
+ assertEquals(m.getFloatValue(), 0f);
+
+ m = new Matcher(Condition.GT, -2.1f);
+ assertEquals(m.getCondition(), Condition.GT);
+ assertEquals(m.getPattern(), "-2.1");
+ assertEquals(m.getFloatValue(), -2.1f);
+
+ m = new Matcher(Condition.NotContains, "-1.2f");
+ assertEquals(m.getCondition(), Condition.NotContains);
+ assertEquals(m.getPattern(), "-1.2F");
+ assertEquals(m.getFloatValue(), 0f);
+
+ m = new Matcher(Condition.GE, "-1.2f");
+ assertEquals(m.getCondition(), Condition.GE);
+ assertEquals(m.getPattern(), "-1.2");
+ assertEquals(m.getFloatValue(), -1.2f);
+
+ try
+ {
+ new Matcher(null, 0f);
+ fail("Expected exception");
+ } catch (NullPointerException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new Matcher(Condition.LT, "123,456");
+ fail("Expected exception");
+ } catch (NumberFormatException e)
+ {
+ // expected
+ }
+ }
+
/**
* Tests for float comparison conditions
*/
assertTrue(m.matches("MOSTLY BENIGN"));
assertTrue(m.matches("pathogenic"));
assertTrue(m.matches(null));
+
+ /*
+ * a float with a string match condition will be treated as string
+ */
+ Matcher m1 = new Matcher(Condition.Contains, "32");
+ assertFalse(m1.matches(-203f));
+ assertTrue(m1.matches(-4321.0f));
}
/**
assertTrue(m.matches("1.0E-7"));
assertFalse(m.matches("0.0000001f"));
}
+
+ @Test
+ public void testToString()
+ {
+ MatcherI m = new Matcher(Condition.LT, 1.2e-6f);
+ assertEquals(m.toString(), "LT 1.2E-6");
+
+ m = new Matcher(Condition.NotMatches, "ABC");
+ assertEquals(m.toString(), "NotMatches ABC");
+
+ m = new Matcher(Condition.Contains, -1.2f);
+ assertEquals(m.toString(), "Contains -1.2");
+ }
+
+ @Test
+ public void testEquals()
+ {
+ /*
+ * string condition
+ */
+ MatcherI m = new Matcher(Condition.NotMatches, "ABC");
+ assertFalse(m.equals(null));
+ assertFalse(m.equals("foo"));
+ assertTrue(m.equals(m));
+ assertTrue(m.equals(new Matcher(Condition.NotMatches, "ABC")));
+ // not case-sensitive:
+ assertTrue(m.equals(new Matcher(Condition.NotMatches, "abc")));
+ assertFalse(m.equals(new Matcher(Condition.Matches, "ABC")));
+ assertFalse(m.equals(new Matcher(Condition.NotMatches, "def")));
+
+ /*
+ * numeric conditions
+ */
+ m = new Matcher(Condition.LT, -1f);
+ assertFalse(m.equals(null));
+ assertFalse(m.equals("foo"));
+ assertTrue(m.equals(m));
+ assertTrue(m.equals(new Matcher(Condition.LT, -1f)));
+ assertTrue(m.equals(new Matcher(Condition.LT, "-1f")));
+ assertTrue(m.equals(new Matcher(Condition.LT, "-1.00f")));
+ assertFalse(m.equals(new Matcher(Condition.LE, -1f)));
+ assertFalse(m.equals(new Matcher(Condition.GE, -1f)));
+ assertFalse(m.equals(new Matcher(Condition.NE, -1f)));
+ assertFalse(m.equals(new Matcher(Condition.LT, 1f)));
+ assertFalse(m.equals(new Matcher(Condition.LT, -1.1f)));
+ }
+
+ @Test
+ public void testHashCode()
+ {
+ MatcherI m1 = new Matcher(Condition.NotMatches, "ABC");
+ MatcherI m2 = new Matcher(Condition.NotMatches, "ABC");
+ MatcherI m3 = new Matcher(Condition.NotMatches, "AB");
+ MatcherI m4 = new Matcher(Condition.Matches, "ABC");
+ assertEquals(m1.hashCode(), m2.hashCode());
+ assertNotEquals(m1.hashCode(), m3.hashCode());
+ assertNotEquals(m1.hashCode(), m4.hashCode());
+ assertNotEquals(m3.hashCode(), m4.hashCode());
+ }
}