JAL-3574 Matcher with integer/long value pattern (e.g. VCF POS locus)
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 13 Apr 2020 11:45:38 +0000 (12:45 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 13 Apr 2020 11:45:38 +0000 (12:45 +0100)
src/jalview/util/matcher/Matcher.java
src/jalview/util/matcher/MatcherI.java
test/jalview/datamodel/features/FeatureMatcherTest.java
test/jalview/gui/FeatureSettingsTest.java
test/jalview/io/FeaturesFileTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/util/matcher/MatcherTest.java

index 0792509..bdf3aa7 100644 (file)
@@ -1,39 +1,49 @@
 package jalview.util.matcher;
 
 import java.util.Objects;
-import java.util.regex.Pattern;
 
 /**
  * A bean to describe one attribute-based filter
  */
 public class Matcher implements MatcherI
 {
+  public enum PatternType
+  {
+    String, Integer, Float
+  }
+
   /*
    * the comparison condition
    */
-  Condition condition;
+  private final Condition condition;
 
   /*
-   * the string pattern as entered, or the regex, to compare to
-   * also holds the string form of float value if a numeric condition
+   * the string pattern as entered, to compare to
    */
-  String pattern;
+  private String pattern;
 
   /*
    * the pattern in upper case, for non-case-sensitive matching
    */
-  String uppercasePattern;
+  private final String uppercasePattern;
 
   /*
    * the compiled regex if using a pattern match condition
-   * (reserved for possible future enhancement)
+   * (possible future enhancement)
    */
-  Pattern regexPattern;
+  // private Pattern regexPattern;
 
   /*
-   * the value to compare to for a numerical condition
+   * the value to compare to for a numerical condition with a float pattern
    */
-  float value;
+  private float floatValue = 0F;
+
+  /*
+   * the value to compare to for a numerical condition with an integer pattern
+   */
+  private long longValue = 0L;
+
+  private PatternType patternType;
 
   /**
    * Constructor
@@ -51,30 +61,38 @@ public class Matcher implements MatcherI
   {
     Objects.requireNonNull(cond);
     condition = cond;
+
     if (cond.isNumeric())
     {
-      value = Float.valueOf(compareTo);
-      pattern = String.valueOf(value);
-      uppercasePattern = pattern;
+      try
+      {
+        longValue = Long.valueOf(compareTo);
+        pattern = String.valueOf(longValue);
+        patternType = PatternType.Integer;
+      } catch (NumberFormatException e)
+      {
+        floatValue = Float.valueOf(compareTo);
+        pattern = String.valueOf(floatValue);
+        patternType = PatternType.Float;
+      }
     }
     else
     {
       pattern = compareTo;
-      if (pattern != null)
-      {
-        uppercasePattern = pattern.toUpperCase();
-      }
+      patternType = PatternType.String;
     }
 
+    uppercasePattern = pattern == null ? null : pattern.toUpperCase();
+
     // if we add regex conditions (e.g. matchesPattern), then
     // pattern should hold the raw regex, and
     // regexPattern = Pattern.compile(compareTo);
   }
 
   /**
-   * Constructor for a numerical match condition. Note that if a string
-   * comparison condition is specified, this will be converted to a comparison
-   * with the float value as string
+   * Constructor for a float-valued numerical match condition. Note that if a
+   * string comparison condition is specified, this will be converted to a
+   * comparison with the float value as string
    * 
    * @param cond
    * @param compareTo
@@ -85,39 +103,56 @@ public class Matcher implements MatcherI
   }
 
   /**
+   * Constructor for an integer-valued numerical match condition. Note that if a
+   * string comparison condition is specified, this will be converted to a
+   * comparison with the integer value as string
+   * 
+   * @param cond
+   * @param compareTo
+   */
+  public Matcher(Condition cond, long compareTo)
+  {
+    this(cond, String.valueOf(compareTo));
+  }
+
+  /**
    * {@inheritDoc}
    */
-  @SuppressWarnings("incomplete-switch")
   @Override
-  public boolean matches(String val)
+  public boolean matches(String compareTo)
   {
-    if (condition.isNumeric())
+    if (compareTo == null)
     {
-      try
-      {
-        /*
-         * treat a null value (no such attribute) as
-         * failing any numerical filter condition
-         */
-        return val == null ? false : matches(Float.valueOf(val));
-      } catch (NumberFormatException e)
-      {
-        return false;
-      }
+      return matchesNull();
     }
-    
-    /*
-     * a null value matches a negative condition, fails a positive test
-     */
-    if (val == null)
+
+    boolean matched = false;
+    switch (patternType)
     {
-      return condition == Condition.NotContains
-              || condition == Condition.NotMatches 
-              || condition == Condition.NotPresent;
+    case Float:
+      matched = matchesFloat(compareTo, floatValue);
+      break;
+    case Integer:
+      matched = matchesLong(compareTo);
+      break;
+    default:
+      matched = matchesString(compareTo);
+      break;
     }
-    
-    String upper = val.toUpperCase().trim();
+    return matched;
+  }
+
+  /**
+   * Executes a non-case-sensitive string comparison to the given value, after
+   * trimming it. Returns true if the test passes, false if it fails.
+   * 
+   * @param compareTo
+   * @return
+   */
+  boolean matchesString(String compareTo)
+  {
     boolean matched = false;
+    String upper = compareTo.toUpperCase().trim();
     switch(condition) {
     case Matches:
       matched = upper.equals(uppercasePattern);
@@ -141,38 +176,48 @@ public class Matcher implements MatcherI
   }
 
   /**
-   * Applies a numerical comparison match condition
+   * Performs a numerical comparison match condition test against a float value
    * 
-   * @param f
+   * @param testee
+   * @param compareTo
    * @return
    */
-  @SuppressWarnings("incomplete-switch")
-  boolean matches(float f)
+  boolean matchesFloat(String testee, float compareTo)
   {
     if (!condition.isNumeric())
     {
-      return matches(String.valueOf(f));
+      // failsafe, shouldn't happen
+      return matches(testee);
+    }
+
+    float f = 0f;
+    try
+    {
+      f = Float.valueOf(testee);
+    } catch (NumberFormatException e)
+    {
+      return false;
     }
     
     boolean matched = false;
     switch (condition) {
     case LT:
-      matched = f < value;
+      matched = f < compareTo;
       break;
     case LE:
-      matched = f <= value;
+      matched = f <= compareTo;
       break;
     case EQ:
-      matched = f == value;
+      matched = f == compareTo;
       break;
     case NE:
-      matched = f != value;
+      matched = f != compareTo;
       break;
     case GT:
-      matched = f > value;
+      matched = f > compareTo;
       break;
     case GE:
-      matched = f >= value;
+      matched = f >= compareTo;
       break;
     default:
       break;
@@ -188,7 +233,7 @@ public class Matcher implements MatcherI
   @Override
   public int hashCode()
   {
-    return pattern.hashCode() + condition.hashCode() + (int) value;
+    return pattern.hashCode() + condition.hashCode() + (int) floatValue;
   }
 
   /**
@@ -203,7 +248,8 @@ public class Matcher implements MatcherI
       return false;
     }
     Matcher m = (Matcher) obj;
-    if (condition != m.condition || value != m.value)
+    if (condition != m.condition || floatValue != m.floatValue
+            || longValue != m.longValue)
     {
       return false;
     }
@@ -227,12 +273,6 @@ public class Matcher implements MatcherI
   }
 
   @Override
-  public float getFloatValue()
-  {
-    return value;
-  }
-
-  @Override
   public String toString()
   {
     StringBuilder sb = new StringBuilder();
@@ -248,4 +288,81 @@ public class Matcher implements MatcherI
 
     return sb.toString();
   }
+
+  /**
+   * Performs a numerical comparison match condition test against an integer
+   * value
+   * 
+   * @param compareTo
+   * @return
+   */
+  boolean matchesLong(String compareTo)
+  {
+    if (!condition.isNumeric())
+    {
+      // failsafe, shouldn't happen
+      return matches(String.valueOf(compareTo));
+    }
+
+    long val = 0L;
+    try
+    {
+      val = Long.valueOf(compareTo);
+    } catch (NumberFormatException e)
+    {
+      /*
+       * try the presented value as a float instead
+       */
+      return matchesFloat(compareTo, longValue);
+    }
+    
+    boolean matched = false;
+    switch (condition) {
+    case LT:
+      matched = val < longValue;
+      break;
+    case LE:
+      matched = val <= longValue;
+      break;
+    case EQ:
+      matched = val == longValue;
+      break;
+    case NE:
+      matched = val != longValue;
+      break;
+    case GT:
+      matched = val > longValue;
+      break;
+    case GE:
+      matched = val >= longValue;
+      break;
+    default:
+      break;
+    }
+  
+    return matched;
+  }
+
+  /**
+   * Tests whether a null value matches the condition. The rule is that any
+   * numeric condition is failed, and only 'negative' string conditions are
+   * matched. So for example <br>
+   * {@code null contains "damaging"}<br>
+   * fails, but <br>
+   * {@code null does not contain "damaging"}</br>
+   * passes.
+   */
+  boolean matchesNull()
+  {
+    if (condition.isNumeric())
+    {
+      return false;
+    }
+    else
+    {
+      return condition == Condition.NotContains
+              || condition == Condition.NotMatches
+              || condition == Condition.NotPresent;
+    }
+  }
 }
index ca6d44c..23e5b27 100644 (file)
@@ -13,6 +13,4 @@ public interface MatcherI
   Condition getCondition();
 
   String getPattern();
-
-  float getFloatValue();
 }
index 4bd34cb..f403a57 100644 (file)
@@ -6,14 +6,17 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
-import jalview.datamodel.SequenceFeature;
-import jalview.util.MessageManager;
-import jalview.util.matcher.Condition;
-
 import java.util.Locale;
 
 import org.testng.annotations.Test;
 
+import jalview.datamodel.SequenceFeature;
+import jalview.util.MessageManager;
+import jalview.util.matcher.Condition;
+import jalview.util.matcher.Matcher;
+import jalview.util.matcher.MatcherI;
+import junit.extensions.PA;
+
 public class FeatureMatcherTest
 {
   @Test(groups = "Functional")
@@ -222,7 +225,7 @@ public class FeatureMatcherTest
     FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2f",
             "AF");
     assertEquals(fm.getMatcher().getCondition(), Condition.GE);
-    assertEquals(fm.getMatcher().getFloatValue(), -2F);
+    assertEquals(PA.getValue(fm.getMatcher(), "floatValue"), -2F);
     assertEquals(fm.getMatcher().getPattern(), "-2.0");
   }
 
@@ -233,71 +236,88 @@ public class FeatureMatcherTest
     assertFalse(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertEquals(fm.getAttribute(), new String[] { "AF" });
-    assertSame(Condition.LT, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getFloatValue(), 1.2f);
-    assertEquals(fm.getMatcher().getPattern(), "1.2");
+    MatcherI matcher = fm.getMatcher();
+    assertSame(Condition.LT, matcher.getCondition());
+    assertEquals(PA.getValue(matcher, "floatValue"), 1.2f);
+    assertSame(PA.getValue(matcher, "patternType"),
+            Matcher.PatternType.Float);
+    assertEquals(matcher.getPattern(), "1.2");
 
     // quotes are optional, condition is not case sensitive
     fm = FeatureMatcher.fromString("AF lt '1.2'");
+    matcher = fm.getMatcher();
     assertFalse(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertEquals(fm.getAttribute(), new String[] { "AF" });
-    assertSame(Condition.LT, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getFloatValue(), 1.2f);
-    assertEquals(fm.getMatcher().getPattern(), "1.2");
+    assertSame(Condition.LT, matcher.getCondition());
+    assertEquals(PA.getValue(matcher, "floatValue"), 1.2F);
+    assertEquals(matcher.getPattern(), "1.2");
 
     fm = FeatureMatcher.fromString("'AF' Present");
+    matcher = fm.getMatcher();
     assertFalse(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertEquals(fm.getAttribute(), new String[] { "AF" });
-    assertSame(Condition.Present, fm.getMatcher().getCondition());
+    assertSame(Condition.Present, matcher.getCondition());
+    assertSame(PA.getValue(matcher, "patternType"),
+            Matcher.PatternType.String);
 
     fm = FeatureMatcher.fromString("CSQ:Consequence contains damaging");
+    matcher = fm.getMatcher();
     assertFalse(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertEquals(fm.getAttribute(), new String[] { "CSQ", "Consequence" });
-    assertSame(Condition.Contains, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "damaging");
+    assertSame(Condition.Contains, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "damaging");
 
     // keyword Label is not case sensitive
     fm = FeatureMatcher.fromString("LABEL Matches 'foobar'");
+    matcher = fm.getMatcher();
     assertTrue(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertNull(fm.getAttribute());
-    assertSame(Condition.Matches, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "foobar");
+    assertSame(Condition.Matches, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "foobar");
 
     fm = FeatureMatcher.fromString("'Label' matches 'foo bar'");
+    matcher = fm.getMatcher();
     assertTrue(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertNull(fm.getAttribute());
-    assertSame(Condition.Matches, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "foo bar");
+    assertSame(Condition.Matches, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "foo bar");
 
     // quotes optional on pattern
     fm = FeatureMatcher.fromString("'Label' matches foo bar");
+    matcher = fm.getMatcher();
     assertTrue(fm.isByLabel());
     assertFalse(fm.isByScore());
     assertNull(fm.getAttribute());
-    assertSame(Condition.Matches, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "foo bar");
+    assertSame(Condition.Matches, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "foo bar");
 
-    fm = FeatureMatcher.fromString("Score GE 12.2");
+    // integer condition
+    fm = FeatureMatcher.fromString("Score GE 12");
+    matcher = fm.getMatcher();
     assertFalse(fm.isByLabel());
     assertTrue(fm.isByScore());
     assertNull(fm.getAttribute());
-    assertSame(Condition.GE, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "12.2");
-    assertEquals(fm.getMatcher().getFloatValue(), 12.2f);
+    assertSame(Condition.GE, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "12");
+    assertEquals(PA.getValue(matcher, "floatValue"), 0f);
+    assertEquals(PA.getValue(matcher, "longValue"), 12L);
+    assertSame(PA.getValue(matcher, "patternType"),
+            Matcher.PatternType.Integer);
 
     // keyword Score is not case sensitive
     fm = FeatureMatcher.fromString("'SCORE' ge '12.2'");
+    matcher = fm.getMatcher();
     assertFalse(fm.isByLabel());
     assertTrue(fm.isByScore());
     assertNull(fm.getAttribute());
-    assertSame(Condition.GE, fm.getMatcher().getCondition());
-    assertEquals(fm.getMatcher().getPattern(), "12.2");
-    assertEquals(fm.getMatcher().getFloatValue(), 12.2f);
+    assertSame(Condition.GE, matcher.getCondition());
+    assertEquals(matcher.getPattern(), "12.2");
+    assertEquals(PA.getValue(matcher, "floatValue"), 12.2F);
 
     // invalid numeric pattern
     assertNull(FeatureMatcher.fromString("Score eq twelve"));
index fd4bd4b..f09c7c7 100644 (file)
@@ -4,6 +4,13 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.testng.annotations.Test;
+
 import jalview.api.FeatureColourI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
@@ -17,13 +24,6 @@ import jalview.schemes.FeatureColourTest;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
-import java.awt.Color;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.testng.annotations.Test;
-
 public class FeatureSettingsTest
 {
   /**
@@ -160,7 +160,7 @@ public class FeatureSettingsTest
     assertEquals(fr.getFeatureFilter("type2").toStableString(),
             "(Score LE 2.4) AND (Score GT 1.1)");
     assertEquals(fr.getFeatureFilter("type3").toStableString(),
-            "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
+            "(AF Contains X) OR (CSQ:PolyPhen NE 0)");
   }
 
   /**
index 298ae6b..b753e94 100644 (file)
@@ -27,6 +27,18 @@ import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureRenderer;
 import jalview.datamodel.Alignment;
@@ -47,18 +59,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
-
-import java.awt.Color;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
+import junit.extensions.PA;
 
 public class FeaturesFileTest
 {
@@ -725,7 +726,7 @@ public class FeaturesFileTest
     assertTrue(matcher.isByScore());
     assertSame(matcher.getMatcher().getCondition(), Condition.LT);
     assertEquals(matcher.getMatcher().getPattern(), "1.3");
-    assertEquals(matcher.getMatcher().getFloatValue(), 1.3f);
+    assertEquals(PA.getValue(matcher.getMatcher(), "floatValue"), 1.3f);
 
     assertFalse(matchers.hasNext());
   }
index 5182ad4..77dee71 100644 (file)
@@ -27,6 +27,21 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JInternalFrame;
+
+import org.testng.Assert;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.analysis.scoremodels.SimilarityParams;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
@@ -75,21 +90,6 @@ import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
-import java.awt.Color;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.JInternalFrame;
-
-import org.testng.Assert;
-import org.testng.AssertJUnit;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
 @Test(singleThreaded = true)
 public class Jalview2xmlTests extends Jalview2xmlBase
 {
@@ -1030,7 +1030,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertEquals(fr.getFeatureFilter("type2").toStableString(),
             "(Score LE 2.4) AND (Score GT 1.1)");
     assertEquals(fr.getFeatureFilter("type3").toStableString(),
-            "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
+            "(AF Contains X) OR (CSQ:PolyPhen NE 0)");
   }
 
   private void addFeature(SequenceI seq, String featureType, int score)
index a47fb60..d29f000 100644 (file)
@@ -3,6 +3,7 @@ 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.assertSame;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -10,6 +11,7 @@ import java.util.Locale;
 
 import org.testng.annotations.Test;
 
+import jalview.util.matcher.Matcher.PatternType;
 import junit.extensions.PA;
 
 public class MatcherTest
@@ -21,22 +23,44 @@ public class MatcherTest
     assertEquals(m.getCondition(), Condition.Contains);
     assertEquals(m.getPattern(), "foo");
     assertEquals(PA.getValue(m, "uppercasePattern"), "FOO");
-    assertEquals(m.getFloatValue(), 0f);
+    assertEquals(PA.getValue(m, "floatValue"), 0f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.String);
 
     m = new Matcher(Condition.GT, -2.1f);
     assertEquals(m.getCondition(), Condition.GT);
     assertEquals(m.getPattern(), "-2.1");
-    assertEquals(m.getFloatValue(), -2.1f);
+    assertEquals(PA.getValue(m, "floatValue"), -2.1f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.Float);
 
     m = new Matcher(Condition.NotContains, "-1.2f");
     assertEquals(m.getCondition(), Condition.NotContains);
     assertEquals(m.getPattern(), "-1.2f");
-    assertEquals(m.getFloatValue(), 0f);
+    assertEquals(PA.getValue(m, "floatValue"), 0f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.String);
 
     m = new Matcher(Condition.GE, "-1.2f");
     assertEquals(m.getCondition(), Condition.GE);
     assertEquals(m.getPattern(), "-1.2");
-    assertEquals(m.getFloatValue(), -1.2f);
+    assertEquals(PA.getValue(m, "floatValue"), -1.2f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.Float);
+
+    m = new Matcher(Condition.GE, "113890813");
+    assertEquals(m.getCondition(), Condition.GE);
+    assertEquals(m.getPattern(), "113890813");
+    assertEquals(PA.getValue(m, "floatValue"), 0f);
+    assertEquals(PA.getValue(m, "longValue"), 113890813L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.Integer);
+
+    m = new Matcher(Condition.GE, "-987f");
+    assertEquals(m.getCondition(), Condition.GE);
+    assertEquals(m.getPattern(), "-987.0");
+    assertEquals(PA.getValue(m, "floatValue"), -987f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.Float);
 
     try
     {
@@ -53,7 +77,25 @@ public class MatcherTest
       fail("Expected exception");
     } catch (NumberFormatException e)
     {
-      // expected
+      // expected - see Long.valueOf()
+    }
+
+    try
+    {
+      new Matcher(Condition.LT, "123_456");
+      fail("Expected exception");
+    } catch (NumberFormatException e)
+    {
+      // expected - see Long.valueOf()
+    }
+
+    try
+    {
+      new Matcher(Condition.LT, "123456L");
+      fail("Expected exception");
+    } catch (NumberFormatException e)
+    {
+      // expected - see Long.valueOf()
     }
   }
 
@@ -82,7 +124,7 @@ public class MatcherTest
     /*
      * >= test
      */
-    m = new Matcher(Condition.GE, 2f);
+    m = new Matcher(Condition.GE, "2f");
     assertTrue(m.matches("2"));
     assertTrue(m.matches("2.1"));
     assertFalse(m.matches("1.9"));
@@ -98,7 +140,7 @@ public class MatcherTest
     /*
      * <= test
      */
-    m = new Matcher(Condition.LE, 2f);
+    m = new Matcher(Condition.LE, "2.0f");
     assertTrue(m.matches("2"));
     assertFalse(m.matches("2.1"));
     assertTrue(m.matches("1.9"));
@@ -112,17 +154,25 @@ public class MatcherTest
     assertTrue(m.matches("1.9"));
   }
 
+  /**
+   * Verifies that all numeric match conditions fail when applied to non-numeric
+   * or null values
+   */
   @Test(groups = "Functional")
-  public void testMatches_floatNullOrInvalid()
+  public void testNumericMatch_nullOrInvalidValue()
   {
     for (Condition cond : Condition.values())
     {
       if (cond.isNumeric())
       {
-        MatcherI m = new Matcher(cond, 2f);
-        assertFalse(m.matches(null));
-        assertFalse(m.matches(""));
-        assertFalse(m.matches("two"));
+        MatcherI m1 = new Matcher(cond, 2.1f);
+        MatcherI m2 = new Matcher(cond, 2345L);
+        assertFalse(m1.matches(null));
+        assertFalse(m1.matches(""));
+        assertFalse(m1.matches("two"));
+        assertFalse(m2.matches(null));
+        assertFalse(m2.matches(""));
+        assertFalse(m2.matches("two"));
       }
     }
   }
@@ -166,7 +216,7 @@ public class MatcherTest
      */
     m = new Matcher(Condition.NotMatches, "benign");
     assertFalse(m.matches("benign"));
-    assertFalse(m.matches(" Benign ")); // trim before testing
+    assertFalse(m.matches(" Benign ")); // trimmed before testing
     assertTrue(m.matches("MOSTLY BENIGN"));
     assertTrue(m.matches("pathogenic"));
     assertTrue(m.matches(null));
@@ -188,11 +238,13 @@ public class MatcherTest
     assertTrue(m.matches(null));
 
     /*
-     * a float with a string match condition will be treated as string
+     * a number 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));
+    assertFalse(m1.matchesFloat("-203f", 0f));
+    assertTrue(m1.matchesFloat("-4321.0f", 0f));
+    assertFalse(m1.matchesFloat("-203", 0f));
+    assertTrue(m1.matchesFloat("-4321", 0f));
   }
 
   /**
@@ -202,9 +254,15 @@ public class MatcherTest
   public void testMatches_floatWithStringCondition()
   {
     MatcherI m = new Matcher(Condition.Contains, 1.2e-6f);
+    assertEquals(m.getPattern(), "1.2E-6");
+    assertEquals(PA.getValue(m, "uppercasePattern"), "1.2E-6");
+    assertEquals(PA.getValue(m, "floatValue"), 0f);
+    assertEquals(PA.getValue(m, "longValue"), 0L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.String);
     assertTrue(m.matches("1.2e-6"));
 
     m = new Matcher(Condition.Contains, 0.0000001f);
+    assertEquals(m.getPattern(), "1.0E-7");
     assertTrue(m.matches("1.0e-7"));
     assertTrue(m.matches("1.0E-7"));
     assertFalse(m.matches("0.0000001f"));
@@ -218,6 +276,9 @@ public class MatcherTest
     MatcherI m = new Matcher(Condition.LT, 1.2e-6f);
     assertEquals(m.toString(), "< 1.2E-6");
 
+    m = new Matcher(Condition.GE, "20200413");
+    assertEquals(m.toString(), ">= 20200413");
+
     m = new Matcher(Condition.NotMatches, "ABC");
     assertEquals(m.toString(), "Does not match 'ABC'");
 
@@ -242,7 +303,7 @@ public class MatcherTest
     assertFalse(m.equals(new Matcher(Condition.NotMatches, "def")));
 
     /*
-     * numeric conditions
+     * numeric conditions - float values
      */
     m = new Matcher(Condition.LT, -1f);
     assertFalse(m.equals(null));
@@ -256,6 +317,18 @@ public class MatcherTest
     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)));
+
+    /*
+     * numeric conditions - integer values
+     */
+    m = new Matcher(Condition.LT, -123456);
+    assertFalse(m.equals(null));
+    assertFalse(m.equals("foo"));
+    assertTrue(m.equals(m));
+    assertTrue(m.equals(new Matcher(Condition.LT, -123456)));
+    assertTrue(m.equals(new Matcher(Condition.LT, "-123456")));
+    assertFalse(m.equals(new Matcher(Condition.LT, -123456f)));
+    assertFalse(m.equals(new Matcher(Condition.LT, "-123456f")));
   }
 
   @Test(groups = "Functional")
@@ -270,4 +343,75 @@ public class MatcherTest
     assertNotEquals(m1.hashCode(), m4.hashCode());
     assertNotEquals(m3.hashCode(), m4.hashCode());
   }
+
+  /**
+   * Tests for integer comparison conditions
+   */
+  @Test(groups = "Functional")
+  public void testMatches_long()
+  {
+    /*
+     * EQUALS test
+     */
+    MatcherI m = new Matcher(Condition.EQ, 2);
+    assertTrue(m.matches("2"));
+    assertTrue(m.matches("+2"));
+    // a float value may be passed to an integer matcher
+    assertTrue(m.matches("2.0"));
+    assertTrue(m.matches("2.000000f"));
+    assertFalse(m.matches("2.01"));
+  
+    /*
+     * NOT EQUALS test
+     */
+    m = new Matcher(Condition.NE, 123);
+    assertFalse(m.matches("123"));
+    assertFalse(m.matches("123.0"));
+    assertTrue(m.matches("-123"));
+  
+    /*
+     * >= test
+     */
+    m = new Matcher(Condition.GE, "113890813");
+    assertTrue(m.matches("113890813"));
+    assertTrue(m.matches("113890814"));
+    assertFalse(m.matches("-113890813"));
+  
+    /*
+     * > test
+     */
+    m = new Matcher(Condition.GT, 113890813);
+    assertFalse(m.matches("113890813"));
+    assertTrue(m.matches("113890814"));
+  
+    /*
+     * <= test
+     */
+    m = new Matcher(Condition.LE, "113890813");
+    assertTrue(m.matches("113890813"));
+    assertFalse(m.matches("113890814"));
+    assertTrue(m.matches("113890812"));
+  
+    /*
+     * < test
+     */
+    m = new Matcher(Condition.LT, 113890813);
+    assertFalse(m.matches("113890813"));
+    assertFalse(m.matches("113890814"));
+    assertTrue(m.matches("113890812"));
+  }
+
+  /**
+   * Tests comparing a float value with an integer condition
+   */
+  @Test(groups = "Functional")
+  public void testMatches_floatValueIntegerCondition()
+  {
+    MatcherI m = new Matcher(Condition.GT, 1234);
+    assertEquals(PA.getValue(m, "longValue"), 1234L);
+    assertSame(PA.getValue(m, "patternType"), PatternType.Integer);
+    assertTrue(m.matches("1235"));
+    assertTrue(m.matches("9867.345"));
+    assertTrue(m.matches("9867.345f"));
+  }
 }