JAL-2808 added conditions Present, NotPresent as options
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 23 Nov 2017 12:50:43 +0000 (12:50 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 23 Nov 2017 12:50:43 +0000 (12:50 +0000)
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/gui/FeatureTypeSettings.java
src/jalview/util/matcher/Condition.java
src/jalview/util/matcher/KeyedMatcher.java
src/jalview/util/matcher/Matcher.java
test/jalview/util/matcher/ConditionTest.java
test/jalview/util/matcher/KeyedMatcherTest.java
test/jalview/util/matcher/MatcherTest.java

index a592282..00888d5 100644 (file)
@@ -1327,6 +1327,8 @@ label.matchCondition_contains = Contains
 label.matchCondition_notcontains = Does not contain
 label.matchCondition_matches = Matches
 label.matchCondition_notmatches = Does not match
+label.matchCondition_present = Is present
+label.matchCondition_notpresent = Is not present
 label.matchCondition_eq = =
 label.matchCondition_ne = not =
 label.matchCondition_lt = <
index 31c3a86..616cb9d 100644 (file)
@@ -1328,6 +1328,8 @@ label.matchCondition_contains = Contiene
 label.matchCondition_notcontains = No contiene
 label.matchCondition_matches = Es igual a
 label.matchCondition_notmatches = No es igual a
+label.matchCondition_present = Está presente
+label.matchCondition_notpresent = No está presente
 label.matchCondition_eq = =
 label.matchCondition_ne = not =
 label.matchCondition_lt = <
index 372234a..1dd12aa 100644 (file)
@@ -47,6 +47,7 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -94,6 +95,9 @@ public class FeatureTypeSettings extends JalviewDialog
 
   private static final int BELOW_THRESHOLD_OPTION = 2;
 
+  private static final DecimalFormat DECFMT_2_2 = new DecimalFormat(
+          "##.##");
+
   /*
    * FeatureRenderer holds colour scheme and filters for feature types
    */
@@ -1238,7 +1242,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * and an empty filter for the user to populate (add)
      */
     KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
-            (String) null);
+            (String[]) null);
     filters.add(noFilter);
 
     /*
@@ -1352,6 +1356,7 @@ public class FeatureTypeSettings extends JalviewDialog
      */
     populateConditions((String) attCombo.getSelectedItem(), cond,
             condCombo);
+    condCombo.setPreferredSize(new Dimension(150, 20));
     condCombo.addItemListener(itemListener);
     filterRow.add(condCombo);
 
@@ -1371,6 +1376,32 @@ public class FeatureTypeSettings extends JalviewDialog
     filterRow.add(patternField);
 
     /*
+     * disable pattern field for condition 'Present / NotPresent'
+     */
+    Condition selectedCondition = (Condition) condCombo.getSelectedItem();
+    if (selectedCondition == Condition.Present
+            || selectedCondition == Condition.NotPresent)
+    {
+      patternField.setEnabled(false);
+    }
+
+    /*
+     * if a numeric condition is selected, show the value range
+     * as a tooltip on the value input field
+     */
+    if (selectedCondition.isNumeric())
+    {
+      float[] minMax = FeatureAttributes.getInstance()
+              .getMinMax(featureType, attName);
+      if (minMax != null)
+      {
+        String tip = String.format("(%s - %s)",
+                DECFMT_2_2.format(minMax[0]), DECFMT_2_2.format(minMax[1]));
+        patternField.setToolTipText(tip);
+      }
+    }
+
+    /*
      * add remove button if filter is populated (non-empty pattern)
      */
     if (pattern != null && pattern.trim().length() > 0)
@@ -1457,8 +1488,8 @@ public class FeatureTypeSettings extends JalviewDialog
    * Answers true unless a numeric condition has been selected with a non-numeric
    * value. Sets the value field to RED with a tooltip if in error.
    * <p>
-   * If the pattern entered is empty, this method returns false, but does not mark
-   * the field as invalid. This supports selecting an attribute for a new
+   * If the pattern is expected but is empty, this method returns false, but does
+   * not mark the field as invalid. This supports selecting an attribute for a new
    * condition before a match pattern has been entered.
    * 
    * @param value
@@ -1473,6 +1504,11 @@ public class FeatureTypeSettings extends JalviewDialog
     }
 
     Condition cond = (Condition) condCombo.getSelectedItem();
+    if (cond == Condition.Present || cond == Condition.NotPresent)
+    {
+      return true;
+    }
+
     value.setBackground(Color.white);
     value.setToolTipText("");
     String v1 = value.getText().trim();
@@ -1553,7 +1589,9 @@ public class FeatureTypeSettings extends JalviewDialog
     for (KeyedMatcherI filter : filters)
     {
       String pattern = filter.getMatcher().getPattern();
-      if (pattern.trim().length() > 0)
+      Condition condition = filter.getMatcher().getCondition();
+      if (pattern.trim().length() > 0 || condition == Condition.Present
+              || condition == Condition.NotPresent)
       {
         if (anded)
         {
index 455f805..4d14063 100644 (file)
@@ -12,6 +12,7 @@ import java.util.Map;
 public enum Condition
 {
   Contains(false), NotContains(false), Matches(false), NotMatches(false),
+  Present(false), NotPresent(false),
   EQ(true), NE(true), LT(true), LE(true), GT(true), GE(true);
 
   private static Map<Condition, String> displayNames = new HashMap<>();
index ef1c702..f21756a 100644 (file)
@@ -73,22 +73,24 @@ public class KeyedMatcher implements KeyedMatcherI
   }
 
   /**
-   * Answers a string description of this matcher, suitable for debugging or
-   * logging. The format may change in future.
+   * Answers a string description of this matcher, suitable for display, debugging
+   * or logging. The format may change in future.
    */
   @Override
   public String toString()
   {
     StringBuilder sb = new StringBuilder();
     sb.append(String.join(COLON, key)).append(" ")
-            .append(matcher.getCondition().toString()).append(" ");
-    if (matcher.getCondition().isNumeric())
+            .append(matcher.getCondition().toString());
+    Condition condition = matcher.getCondition();
+    if (condition.isNumeric())
     {
-      sb.append(matcher.getPattern());
+      sb.append(" ").append(matcher.getPattern());
     }
-    else
+    else if (condition != Condition.Present
+            && condition != Condition.NotPresent)
     {
-      sb.append("'").append(matcher.getPattern()).append("'");
+      sb.append(" '").append(matcher.getPattern()).append("'");
     }
 
     return sb.toString();
index 715694c..14a8585 100644 (file)
@@ -53,7 +53,7 @@ public class Matcher implements MatcherI
     else
     {
       // pattern matches will be non-case-sensitive
-      pattern = compareTo.toUpperCase();
+      pattern = compareTo == null ? null : compareTo.toUpperCase();
     }
 
     // if we add regex conditions (e.g. matchesPattern), then
@@ -105,7 +105,8 @@ public class Matcher implements MatcherI
     if (val == null)
     {
       return condition == Condition.NotContains
-              || condition == Condition.NotMatches;
+              || condition == Condition.NotMatches 
+              || condition == Condition.NotPresent;
     }
     
     String upper = val.toUpperCase().trim();
@@ -123,6 +124,11 @@ public class Matcher implements MatcherI
     case NotContains:
       matched = upper.indexOf(pattern) == -1;
       break;
+    case Present:
+      matched = true;
+      break;
+    default:
+      break;
     }
     return matched;
   }
@@ -161,6 +167,8 @@ public class Matcher implements MatcherI
     case GE:
       matched = f >= value;
       break;
+    default:
+      break;
     }
 
     return matched;
index 9d8b225..883596a 100644 (file)
@@ -16,6 +16,8 @@ public class ConditionTest
     assertEquals(Condition.NotContains.toString(), "Does not contain");
     assertEquals(Condition.Matches.toString(), "Matches");
     assertEquals(Condition.NotMatches.toString(), "Does not match");
+    assertEquals(Condition.Present.toString(), "Is present");
+    assertEquals(Condition.NotPresent.toString(), "Is not present");
     assertEquals(Condition.LT.toString(), "<");
     assertEquals(Condition.LE.toString(), "<=");
     assertEquals(Condition.GT.toString(), ">");
index 01d0067..3fd7800 100644 (file)
@@ -39,6 +39,14 @@ public class KeyedMatcherTest
      */
     KeyedMatcherI km = new KeyedMatcher(Condition.LT, 1.2f, "AF");
     assertEquals(km.toString(), "AF < 1.2");
+
+    /*
+     * Present / NotPresent omit the value pattern
+     */
+    km = new KeyedMatcher(Condition.Present, "", "AF");
+    assertEquals(km.toString(), "AF Is present");
+    km = new KeyedMatcher(Condition.NotPresent, "", "AF");
+    assertEquals(km.toString(), "AF Is not present");
   }
 
   @Test
index ee0ff82..f4c8181 100644 (file)
@@ -167,6 +167,22 @@ public class MatcherTest
     assertTrue(m.matches(null));
 
     /*
+     * value is present (is not null)
+     */
+    m = new Matcher(Condition.Present, null);
+    assertTrue(m.matches("benign"));
+    assertTrue(m.matches(""));
+    assertFalse(m.matches(null));
+
+    /*
+     * value is not present (is null)
+     */
+    m = new Matcher(Condition.NotPresent, null);
+    assertFalse(m.matches("benign"));
+    assertFalse(m.matches(""));
+    assertTrue(m.matches(null));
+
+    /*
      * a float with a string match condition will be treated as string
      */
     Matcher m1 = new Matcher(Condition.Contains, "32");