From 4e980600b458ea652d357b844cd92118a02c8336 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 15 Nov 2017 10:36:23 +0000 Subject: [PATCH] JAL- 2835 support filter on nested attribute keys --- src/jalview/util/matcher/KeyedMatcher.java | 20 ++--- src/jalview/util/matcher/KeyedMatcherI.java | 4 +- src/jalview/util/matcher/KeyedMatcherSet.java | 2 +- src/jalview/util/matcher/KeyedMatcherSetI.java | 2 +- src/jalview/util/matcher/Matcher.java | 4 +- test/jalview/util/matcher/ConditionTest.java | 2 +- test/jalview/util/matcher/KeyedMatcherSetTest.java | 80 ++++++++++++++------ test/jalview/util/matcher/KeyedMatcherTest.java | 23 ++++-- test/jalview/util/matcher/MatcherTest.java | 16 ++-- 9 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/jalview/util/matcher/KeyedMatcher.java b/src/jalview/util/matcher/KeyedMatcher.java index cd952e7..ef1c702 100644 --- a/src/jalview/util/matcher/KeyedMatcher.java +++ b/src/jalview/util/matcher/KeyedMatcher.java @@ -19,18 +19,20 @@ import java.util.function.Function; */ public class KeyedMatcher implements KeyedMatcherI { - final private String key; + private static final String COLON = ":"; + + final private String[] key; final private MatcherI matcher; /** * Constructor given a key, a test condition and a match pattern * - * @param theKey * @param cond * @param pattern + * @param theKey */ - public KeyedMatcher(String theKey, Condition cond, String pattern) + public KeyedMatcher(Condition cond, String pattern, String... theKey) { key = theKey; matcher = new Matcher(cond, pattern); @@ -41,25 +43,25 @@ public class KeyedMatcher implements KeyedMatcherI * to. Note that if a non-numerical condition is specified, the float will be * converted to a string. * - * @param theKey * @param cond * @param value + * @param theKey */ - public KeyedMatcher(String theKey, Condition cond, float value) + public KeyedMatcher(Condition cond, float value, String... theKey) { key = theKey; matcher = new Matcher(cond, value); } @Override - public boolean matches(Function valueProvider) + public boolean matches(Function valueProvider) { String value = valueProvider.apply(key); return matcher.matches(value); } @Override - public String getKey() + public String[] getKey() { return key; } @@ -78,8 +80,8 @@ public class KeyedMatcher implements KeyedMatcherI public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(key).append(" ").append(matcher.getCondition().toString()) - .append(" "); + sb.append(String.join(COLON, key)).append(" ") + .append(matcher.getCondition().toString()).append(" "); if (matcher.getCondition().isNumeric()) { sb.append(matcher.getPattern()); diff --git a/src/jalview/util/matcher/KeyedMatcherI.java b/src/jalview/util/matcher/KeyedMatcherI.java index e9fe014..e8d71c1 100644 --- a/src/jalview/util/matcher/KeyedMatcherI.java +++ b/src/jalview/util/matcher/KeyedMatcherI.java @@ -18,14 +18,14 @@ public interface KeyedMatcherI * @param valueProvider * @return */ - boolean matches(Function valueProvider); + boolean matches(Function valueProvider); /** * Answers the value key this matcher operates on * * @return */ - String getKey(); + String[] getKey(); /** * Answers the match condition that is applied diff --git a/src/jalview/util/matcher/KeyedMatcherSet.java b/src/jalview/util/matcher/KeyedMatcherSet.java index 35a41c2..a4be48a 100644 --- a/src/jalview/util/matcher/KeyedMatcherSet.java +++ b/src/jalview/util/matcher/KeyedMatcherSet.java @@ -19,7 +19,7 @@ public class KeyedMatcherSet implements KeyedMatcherSetI } @Override - public boolean matches(Function valueProvider) + public boolean matches(Function valueProvider) { /* * no conditions matches anything diff --git a/src/jalview/util/matcher/KeyedMatcherSetI.java b/src/jalview/util/matcher/KeyedMatcherSetI.java index 25dc96e..3e9f5b6 100644 --- a/src/jalview/util/matcher/KeyedMatcherSetI.java +++ b/src/jalview/util/matcher/KeyedMatcherSetI.java @@ -18,7 +18,7 @@ public interface KeyedMatcherSetI * @param valueProvider * @return */ - boolean matches(Function valueProvider); + boolean matches(Function valueProvider); /** * Answers a new object that matches the logical AND of this and m diff --git a/src/jalview/util/matcher/Matcher.java b/src/jalview/util/matcher/Matcher.java index a213a17..715694c 100644 --- a/src/jalview/util/matcher/Matcher.java +++ b/src/jalview/util/matcher/Matcher.java @@ -37,8 +37,8 @@ public class Matcher implements MatcherI * @param compareTo * @return * @throws NumberFormatException - * if a numerical condition is specified with a non-numeric - * comparision value + * if a numerical condition is specified with a non-numeric comparison + * value * @throws NullPointerException * if a null condition or comparison string is specified */ diff --git a/test/jalview/util/matcher/ConditionTest.java b/test/jalview/util/matcher/ConditionTest.java index 270aa2a..9d8b225 100644 --- a/test/jalview/util/matcher/ConditionTest.java +++ b/test/jalview/util/matcher/ConditionTest.java @@ -8,7 +8,7 @@ import org.testng.annotations.Test; public class ConditionTest { - @Test + @Test(groups = "Functional") public void testToString() { Locale.setDefault(Locale.UK); diff --git a/test/jalview/util/matcher/KeyedMatcherSetTest.java b/test/jalview/util/matcher/KeyedMatcherSetTest.java index 3018cb6..3d597d2 100644 --- a/test/jalview/util/matcher/KeyedMatcherSetTest.java +++ b/test/jalview/util/matcher/KeyedMatcherSetTest.java @@ -12,13 +12,13 @@ import org.testng.annotations.Test; public class KeyedMatcherSetTest { - @Test + @Test(groups = "Functional") public void testMatches() { /* * a numeric matcher - MatcherTest covers more conditions */ - KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F); + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "AF"); KeyedMatcherSetI kms = new KeyedMatcherSet(); kms.and(km); assertTrue(kms.matches(key -> "-2")); @@ -31,24 +31,25 @@ public class KeyedMatcherSetTest /* * a string pattern matcher */ - km = new KeyedMatcher("AF", Condition.Contains, "Cat"); + km = new KeyedMatcher(Condition.Contains, "Cat", "AF"); kms = new KeyedMatcherSet(); kms.and(km); assertTrue(kms - .matches(key -> "AF".equals(key) ? "raining cats and dogs" + .matches(key -> "AF".equals(key[0]) ? "raining cats and dogs" : "showers")); } - @Test + @Test(groups = "Functional") public void testAnd() { // condition1: AF value contains "dog" (matches) - KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.Contains, "dog"); + KeyedMatcherI km1 = new KeyedMatcher(Condition.Contains, "dog", "AF"); // condition 2: CSQ value does not contain "how" (does not match) - KeyedMatcherI km2 = new KeyedMatcher("CSQ", Condition.NotContains, - "how"); + KeyedMatcherI km2 = new KeyedMatcher(Condition.NotContains, "how", + "CSQ"); - Function vp = key -> "AF".equals(key) ? "raining cats and dogs" + Function vp = key -> "AF".equals(key[0]) + ? "raining cats and dogs" : "showers"; assertTrue(km1.matches(vp)); assertFalse(km2.matches(vp)); @@ -61,13 +62,14 @@ public class KeyedMatcherSetTest assertFalse(kms.matches(vp)); } - @Test + @Test(groups = "Functional") public void testToString() { - KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.LT, 1.2f); + KeyedMatcherI km1 = new KeyedMatcher(Condition.LT, 1.2f, "AF"); assertEquals(km1.toString(), "AF < 1.2"); - KeyedMatcher km2 = new KeyedMatcher("CLIN_SIG", Condition.NotContains, "path"); + KeyedMatcher km2 = new KeyedMatcher(Condition.NotContains, "path", + "CLIN_SIG"); assertEquals(km2.toString(), "CLIN_SIG Does not contain 'PATH'"); /* @@ -93,16 +95,17 @@ public class KeyedMatcherSetTest "(AF < 1.2) OR (CLIN_SIG Does not contain 'PATH')"); } - @Test + @Test(groups = "Functional") public void testOr() { // condition1: AF value contains "dog" (matches) - KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.Contains, "dog"); + KeyedMatcherI km1 = new KeyedMatcher(Condition.Contains, "dog", "AF"); // condition 2: CSQ value does not contain "how" (does not match) - KeyedMatcherI km2 = new KeyedMatcher("CSQ", Condition.NotContains, - "how"); + KeyedMatcherI km2 = new KeyedMatcher(Condition.NotContains, "how", + "CSQ"); - Function vp = key -> "AF".equals(key) ? "raining cats and dogs" + Function vp = key -> "AF".equals(key[0]) + ? "raining cats and dogs" : "showers"; assertTrue(km1.matches(vp)); assertFalse(km2.matches(vp)); @@ -114,17 +117,17 @@ public class KeyedMatcherSetTest assertTrue(kms.matches(vp)); } - @Test + @Test(groups = "Functional") public void testIsEmpty() { - KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F); + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "AF"); KeyedMatcherSetI kms = new KeyedMatcherSet(); assertTrue(kms.isEmpty()); kms.and(km); assertFalse(kms.isEmpty()); } - @Test + @Test(groups = "Functional") public void testGetMatchers() { KeyedMatcherSetI kms = new KeyedMatcherSet(); @@ -138,7 +141,7 @@ public class KeyedMatcherSetTest /* * one matcher: */ - KeyedMatcherI km1 = new KeyedMatcher("AF", Condition.GE, -2F); + KeyedMatcherI km1 = new KeyedMatcher(Condition.GE, -2F, "AF"); kms.and(km1); iterator = kms.getMatchers().iterator(); assertSame(km1, iterator.next()); @@ -147,11 +150,44 @@ public class KeyedMatcherSetTest /* * two matchers: */ - KeyedMatcherI km2 = new KeyedMatcher("AF", Condition.LT, 8F); + KeyedMatcherI km2 = new KeyedMatcher(Condition.LT, 8F, "AF"); kms.and(km2); iterator = kms.getMatchers().iterator(); assertSame(km1, iterator.next()); assertSame(km2, iterator.next()); assertFalse(iterator.hasNext()); } + + /** + * Tests for the 'compound attribute' key i.e. where first key's value is a map + * from which we take the value for the second key, e.g. CSQ : Consequence + */ + @Test(groups = "Functional") + public void testMatches_compoundKey() + { + /* + * a numeric matcher - MatcherTest covers more conditions + */ + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "CSQ", + "Consequence"); + KeyedMatcherSetI kms = new KeyedMatcherSet(); + kms.and(km); + assertTrue(kms.matches(key -> "-2")); + assertTrue(kms.matches(key -> "-1")); + assertFalse(kms.matches(key -> "-3")); + assertFalse(kms.matches(key -> "")); + assertFalse(kms.matches(key -> "junk")); + assertFalse(kms.matches(key -> null)); + + /* + * a string pattern matcher + */ + km = new KeyedMatcher(Condition.Contains, "Cat", "CSQ", "Consequence"); + kms = new KeyedMatcherSet(); + kms.and(km); + assertTrue(kms.matches(key -> "csq".equalsIgnoreCase(key[0]) + && "Consequence".equalsIgnoreCase(key[1]) + ? "raining cats and dogs" + : "showers")); + } } diff --git a/test/jalview/util/matcher/KeyedMatcherTest.java b/test/jalview/util/matcher/KeyedMatcherTest.java index 164b8eb..01d0067 100644 --- a/test/jalview/util/matcher/KeyedMatcherTest.java +++ b/test/jalview/util/matcher/KeyedMatcherTest.java @@ -14,7 +14,7 @@ public class KeyedMatcherTest /* * a numeric matcher - MatcherTest covers more conditions */ - KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F); + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "AF"); assertTrue(km.matches(key -> "-2")); assertTrue(km.matches(key -> "-1")); assertFalse(km.matches(key -> "-3")); @@ -25,9 +25,10 @@ public class KeyedMatcherTest /* * a string pattern matcher */ - km = new KeyedMatcher("AF", Condition.Contains, "Cat"); - assertTrue(km.matches(key -> "AF".equals(key) ? "raining cats and dogs" - : "showers")); + km = new KeyedMatcher(Condition.Contains, "Cat", "AF"); + assertTrue( + km.matches(key -> "AF".equals(key[0]) ? "raining cats and dogs" + : "showers")); } @Test @@ -36,21 +37,27 @@ public class KeyedMatcherTest /* * toString uses the i18n translation of the enum conditions */ - KeyedMatcherI km = new KeyedMatcher("AF", Condition.LT, 1.2f); + KeyedMatcherI km = new KeyedMatcher(Condition.LT, 1.2f, "AF"); assertEquals(km.toString(), "AF < 1.2"); } @Test public void testGetKey() { - KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F); - assertEquals(km.getKey(), "AF"); + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "AF"); + assertEquals(km.getKey(), new String[] { "AF" }); + + /* + * compound key (attribute / subattribute) + */ + km = new KeyedMatcher(Condition.GE, -2F, "CSQ", "Consequence"); + assertEquals(km.getKey(), new String[] { "CSQ", "Consequence" }); } @Test public void testGetMatcher() { - KeyedMatcherI km = new KeyedMatcher("AF", Condition.GE, -2F); + KeyedMatcherI km = new KeyedMatcher(Condition.GE, -2F, "AF"); assertEquals(km.getMatcher().getCondition(), Condition.GE); assertEquals(km.getMatcher().getFloatValue(), -2F); assertEquals(km.getMatcher().getPattern(), "-2.0"); diff --git a/test/jalview/util/matcher/MatcherTest.java b/test/jalview/util/matcher/MatcherTest.java index 489cdce..ee0ff82 100644 --- a/test/jalview/util/matcher/MatcherTest.java +++ b/test/jalview/util/matcher/MatcherTest.java @@ -10,7 +10,7 @@ import org.testng.annotations.Test; public class MatcherTest { - @Test + @Test(groups = "Functional") public void testConstructor() { MatcherI m = new Matcher(Condition.Contains, "foo"); @@ -55,7 +55,7 @@ public class MatcherTest /** * Tests for float comparison conditions */ - @Test + @Test(groups = "Functional") public void testMatches_float() { /* @@ -107,7 +107,7 @@ public class MatcherTest assertTrue(m.matches("1.9")); } - @Test + @Test(groups = "Functional") public void testMatches_floatNullOrInvalid() { for (Condition cond : Condition.values()) @@ -125,7 +125,7 @@ public class MatcherTest /** * Tests for string comparison conditions */ - @Test + @Test(groups = "Functional") public void testMatches_pattern() { /* @@ -177,7 +177,7 @@ public class MatcherTest /** * If a float is passed with a string condition it gets converted to a string */ - @Test + @Test(groups = "Functional") public void testMatches_floatWithStringCondition() { MatcherI m = new Matcher(Condition.Contains, 1.2e-6f); @@ -189,7 +189,7 @@ public class MatcherTest assertFalse(m.matches("0.0000001f")); } - @Test + @Test(groups = "Functional") public void testToString() { MatcherI m = new Matcher(Condition.LT, 1.2e-6f); @@ -202,7 +202,7 @@ public class MatcherTest assertEquals(m.toString(), "Contains '-1.2'"); } - @Test + @Test(groups = "Functional") public void testEquals() { /* @@ -235,7 +235,7 @@ public class MatcherTest assertFalse(m.equals(new Matcher(Condition.LT, -1.1f))); } - @Test + @Test(groups = "Functional") public void testHashCode() { MatcherI m1 = new Matcher(Condition.NotMatches, "ABC"); -- 1.7.10.2