JAL- 2835 support filter on nested attribute keys
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 15 Nov 2017 10:36:23 +0000 (10:36 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 15 Nov 2017 10:36:23 +0000 (10:36 +0000)
src/jalview/util/matcher/KeyedMatcher.java
src/jalview/util/matcher/KeyedMatcherI.java
src/jalview/util/matcher/KeyedMatcherSet.java
src/jalview/util/matcher/KeyedMatcherSetI.java
src/jalview/util/matcher/Matcher.java
test/jalview/util/matcher/ConditionTest.java
test/jalview/util/matcher/KeyedMatcherSetTest.java
test/jalview/util/matcher/KeyedMatcherTest.java
test/jalview/util/matcher/MatcherTest.java

index cd952e7..ef1c702 100644 (file)
@@ -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<String, String> valueProvider)
+  public boolean matches(Function<String[], String> 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());
index e9fe014..e8d71c1 100644 (file)
@@ -18,14 +18,14 @@ public interface KeyedMatcherI
    * @param valueProvider
    * @return
    */
-  boolean matches(Function<String, String> valueProvider);
+  boolean matches(Function<String[], String> valueProvider);
 
   /**
    * Answers the value key this matcher operates on
    * 
    * @return
    */
-  String getKey();
+  String[] getKey();
 
   /**
    * Answers the match condition that is applied
index 35a41c2..a4be48a 100644 (file)
@@ -19,7 +19,7 @@ public class KeyedMatcherSet implements KeyedMatcherSetI
   }
 
   @Override
-  public boolean matches(Function<String, String> valueProvider)
+  public boolean matches(Function<String[], String> valueProvider)
   {
     /*
      * no conditions matches anything
index 25dc96e..3e9f5b6 100644 (file)
@@ -18,7 +18,7 @@ public interface KeyedMatcherSetI
    * @param valueProvider
    * @return
    */
-  boolean matches(Function<String, String> valueProvider);
+  boolean matches(Function<String[], String> valueProvider);
 
   /**
    * Answers a new object that matches the logical AND of this and m
index a213a17..715694c 100644 (file)
@@ -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
    */
index 270aa2a..9d8b225 100644 (file)
@@ -8,7 +8,7 @@ import org.testng.annotations.Test;
 
 public class ConditionTest
 {
-  @Test
+  @Test(groups = "Functional")
   public void testToString()
   {
     Locale.setDefault(Locale.UK);
index 3018cb6..3d597d2 100644 (file)
@@ -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<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
+    Function<String[], String> 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<String, String> vp = key -> "AF".equals(key) ? "raining cats and dogs"
+    Function<String[], String> 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"));
+  }
 }
index 164b8eb..01d0067 100644 (file)
@@ -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");
index 489cdce..ee0ff82 100644 (file)
@@ -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");