JAL-2808 update spike to latest
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 24 Nov 2017 14:50:45 +0000 (14:50 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 24 Nov 2017 14:50:45 +0000 (14:50 +0000)
src/jalview/datamodel/features/FeatureMatcher.java
src/jalview/datamodel/features/FeatureMatcherI.java
src/jalview/datamodel/features/FeatureMatcherSet.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/util/matcher/Condition.java
src/jalview/util/matcher/Matcher.java
test/jalview/datamodel/features/FeatureMatcherSetTest.java
test/jalview/datamodel/features/FeatureMatcherTest.java
test/jalview/util/matcher/MatcherTest.java

index 1fc0e0f..b86468d 100644 (file)
@@ -1,6 +1,7 @@
 package jalview.datamodel.features;
 
 import jalview.datamodel.SequenceFeature;
+import jalview.util.MessageManager;
 import jalview.util.matcher.Condition;
 import jalview.util.matcher.Matcher;
 import jalview.util.matcher.MatcherI;
@@ -22,6 +23,12 @@ import jalview.util.matcher.MatcherI;
  */
 public class FeatureMatcher implements FeatureMatcherI
 {
+  /*
+   * a dummy matcher that comes in useful for the 'add a filter' gui row
+   */
+  public static final FeatureMatcherI NULL_MATCHER = FeatureMatcher
+          .byLabel(Condition.values()[0], "");
+
   private static final String COLON = ":";
 
   /*
@@ -103,7 +110,7 @@ public class FeatureMatcher implements FeatureMatcherI
   }
 
   @Override
-  public String[] getKey()
+  public String[] getAttribute()
   {
     return key;
   }
@@ -122,9 +129,21 @@ public class FeatureMatcher implements FeatureMatcherI
   public String toString()
   {
     StringBuilder sb = new StringBuilder();
-    sb.append(String.join(COLON, key)).append(" ")
-            .append(matcher.getCondition().toString());
+    if (byLabel)
+    {
+      sb.append(MessageManager.getString("label.label"));
+    }
+    else if (byScore)
+    {
+      sb.append(MessageManager.getString("label.score"));
+    }
+    else
+    {
+      sb.append(String.join(COLON, key));
+    }
+
     Condition condition = matcher.getCondition();
+    sb.append(" ").append(condition.toString().toLowerCase());
     if (condition.isNumeric())
     {
       sb.append(" ").append(matcher.getPattern());
@@ -136,4 +155,16 @@ public class FeatureMatcher implements FeatureMatcherI
 
     return sb.toString();
   }
+
+  @Override
+  public boolean isByLabel()
+  {
+    return byLabel;
+  }
+
+  @Override
+  public boolean isByScore()
+  {
+    return byScore;
+  }
 }
index 078f4a4..07b060c 100644 (file)
@@ -21,11 +21,26 @@ public interface FeatureMatcherI
   boolean matches(SequenceFeature feature);
 
   /**
-   * Answers the value key this matcher operates on
+   * Answers the attribute key this matcher operates on (or null if match is by
+   * Label or Score)
    * 
    * @return
    */
-  String[] getKey();
+  String[] getAttribute();
+
+  /**
+   * Answers true if match is against feature label (description), else false
+   * 
+   * @return
+   */
+  boolean isByLabel();
+
+  /**
+   * Answers true if match is against feature score, else false
+   * 
+   * @return
+   */
+  boolean isByScore();
 
   /**
    * Answers the match condition that is applied
index 64ae61b..eb55387 100644 (file)
@@ -1,12 +1,19 @@
 package jalview.datamodel.features;
 
 import jalview.datamodel.SequenceFeature;
+import jalview.util.MessageManager;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class FeatureMatcherSet implements FeatureMatcherSetI
 {
+  private static final String OR_I18N = MessageManager
+          .getString("label.or");
+
+  private static final String AND_18N = MessageManager
+          .getString("label.and");
+
   List<FeatureMatcherI> matchConditions;
 
   boolean andConditions;
@@ -105,7 +112,8 @@ public class FeatureMatcherSet implements FeatureMatcherSetI
     {
       if (!first)
       {
-        sb.append(andConditions ? " AND " : " OR ");
+        String joiner = andConditions ? AND_18N : OR_I18N;
+        sb.append(" ").append(joiner.toLowerCase()).append(" ");
       }
       first = false;
       sb.append("(").append(matcher.toString()).append(")");
index 0c4cd56..4b4f363 100644 (file)
  */
 package jalview.gui;
 
-import static jalview.gui.FeatureSettings.FeatureTableModel.COLOUR_COLUMN;
-import static jalview.gui.FeatureSettings.FeatureTableModel.FILTER_COLUMN;
-import static jalview.gui.FeatureSettings.FeatureTableModel.SHOW_COLUMN;
-import static jalview.gui.FeatureSettings.FeatureTableModel.TYPE_COLUMN;
-
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.bin.Cache;
@@ -110,6 +105,17 @@ import javax.swing.table.TableColumn;
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
+  /*
+   * column indices of fields in Feature Settings table
+   */
+  static final int TYPE_COLUMN = 0;
+
+  static final int COLOUR_COLUMN = 1;
+
+  static final int FILTER_COLUMN = 2;
+
+  static final int SHOW_COLUMN = 3;
+
   private static final int COLUMN_COUNT = 4;
 
   private static final int MIN_WIDTH = 400;
@@ -648,8 +654,7 @@ public class FeatureSettings extends JPanel
       }
     }
 
-    int columnCount = COLUMN_COUNT;
-    Object[][] data = new Object[displayableTypes.size()][columnCount];
+    Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
     int dataIndex = 0;
 
     if (fr.hasRenderOrder())
@@ -712,11 +717,10 @@ public class FeatureSettings extends JPanel
 
     if (originalData == null)
     {
-      int size = data[0].length;
-      originalData = new Object[data.length][size];
+      originalData = new Object[data.length][COLUMN_COUNT];
       for (int i = 0; i < data.length; i++)
       {
-        System.arraycopy(data[i], 0, originalData[i], 0, size);
+        System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
       }
     }
     else
@@ -787,11 +791,12 @@ public class FeatureSettings extends JPanel
         /*
          * new feature detected - add to original data (on top)
          */
-        int size = currentData[0].length;
-        Object[][] newData = new Object[originalData.length + 1][size];
+        Object[][] newData = new Object[originalData.length
+                + 1][COLUMN_COUNT];
         for (int i = 0; i < originalData.length; i++)
         {
-          System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
+          System.arraycopy(originalData[i], 0, newData[i + 1], 0,
+                  COLUMN_COUNT);
         }
         newData[0] = row;
         originalData = newData;
@@ -1509,17 +1514,6 @@ public class FeatureSettings extends JPanel
   // ///////////////////////////////////////////////////////////////////////
   class FeatureTableModel extends AbstractTableModel
   {
-    /*
-     * column indices of fields in Feature Settings table
-     */
-    static final int TYPE_COLUMN = 0;
-
-    static final int COLOUR_COLUMN = 1;
-
-    static final int FILTER_COLUMN = 2;
-
-    static final int SHOW_COLUMN = 3;
-
     private String[] columnNames = {
         MessageManager.getString("label.feature_type"),
         MessageManager.getString("action.colour"),
index 835f1fc..c356731 100644 (file)
@@ -38,7 +38,7 @@ import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
-import java.awt.LayoutManager;
+import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusAdapter;
@@ -79,6 +79,12 @@ import javax.swing.plaf.basic.BasicArrowButton;
  */
 public class FeatureTypeSettings extends JalviewDialog
 {
+  private final static String LABEL_18N = MessageManager
+          .getString("label.label");
+
+  private final static String SCORE_18N = MessageManager
+          .getString("label.score");
+
   private static final int RADIO_WIDTH = 130;
 
   private static final String COLON = ":";
@@ -294,8 +300,7 @@ public class FeatureTypeSettings extends JalviewDialog
         }
         else
         {
-          colourByTextCombo
-                  .setSelectedItem(MessageManager.getString("label.label"));
+          colourByTextCombo.setSelectedItem(LABEL_18N);
         }
       }
       else
@@ -336,8 +341,7 @@ public class FeatureTypeSettings extends JalviewDialog
       }
       else
       {
-        colourByRangeCombo
-                .setSelectedItem(MessageManager.getString("label.score"));
+        colourByRangeCombo.setSelectedItem(SCORE_18N);
       }
       Color noColour = fc.getNoColour();
       if (noColour == null)
@@ -444,8 +448,7 @@ public class FeatureTypeSettings extends JalviewDialog
     }
 
     String colourBy = (String) colourByRangeCombo.getSelectedItem();
-    String[] attNames = fromAttributeDisplayName(colourBy);
-    float[] minMax = getMinMax(attNames);
+    float[] minMax = getMinMax(colourBy);
 
     if (minMax != null)
     {
@@ -461,13 +464,13 @@ public class FeatureTypeSettings extends JalviewDialog
    * <li>else of the selected attribute</li>
    * </ul>
    * 
-   * @param attNames
+   * @param attName
    * @return
    */
-  private float[] getMinMax(String[] attNames)
+  private float[] getMinMax(String attName)
   {
     float[] minMax = null;
-    if (MessageManager.getString("label.score").equals(attNames[0]))
+    if (SCORE_18N.equals(attName))
     {
       minMax = fr.getMinMax().get(featureType)[0];
     }
@@ -475,7 +478,7 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       // colour by attribute range
       minMax = FeatureAttributes.getInstance().getMinMax(featureType,
-              attNames);
+              fromAttributeDisplayName(attName));
     }
     return minMax;
   }
@@ -902,7 +905,7 @@ public class FeatureTypeSettings extends JalviewDialog
       FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
       fc.setColourByLabel(true);
       String byWhat = (String) colourByTextCombo.getSelectedItem();
-      if (!MessageManager.getString("label.label").equals(byWhat))
+      if (!LABEL_18N.equals(byWhat))
       {
         fc.setAttributeName(fromAttributeDisplayName(byWhat));
       }
@@ -959,7 +962,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * set attribute to colour by if selected
      */
     String byWhat = (String) colourByRangeCombo.getSelectedItem();
-    if (!MessageManager.getString("label.score").equals(byWhat))
+    if (!SCORE_18N.equals(byWhat))
     {
       fc.setAttributeName(fromAttributeDisplayName(byWhat));
     }
@@ -1134,7 +1137,7 @@ public class FeatureTypeSettings extends JalviewDialog
 
     if (withText)
     {
-      displayAtts.add(MessageManager.getString("label.label"));
+      displayAtts.add(LABEL_18N);
       tooltips.add(MessageManager.getString("label.description"));
     }
     if (withRange)
@@ -1142,8 +1145,8 @@ public class FeatureTypeSettings extends JalviewDialog
       float[][] minMax = fr.getMinMax().get(featureType);
       if (minMax != null && minMax[0][0] != minMax[0][1])
       {
-        displayAtts.add(MessageManager.getString("label.score"));
-        tooltips.add(MessageManager.getString("label.score"));
+        displayAtts.add(SCORE_18N);
+        tooltips.add(SCORE_18N);
       }
     }
 
@@ -1188,11 +1191,11 @@ public class FeatureTypeSettings extends JalviewDialog
     filtersPanel.add(andOrPanel);
 
     /*
-     * panel with filters - populated by refreshFiltersDisplay
+     * panel with filters - populated by refreshFiltersDisplay, 
+     * which also sets the layout manager
      */
     chooseFiltersPanel = new JPanel();
-    LayoutManager box = new BoxLayout(chooseFiltersPanel, BoxLayout.Y_AXIS);
-    chooseFiltersPanel.setLayout(box);
+    chooseFiltersPanel.setBackground(Color.white);
     filtersPanel.add(chooseFiltersPanel);
 
     return filtersPanel;
@@ -1267,9 +1270,21 @@ public class FeatureTypeSettings extends JalviewDialog
     /*
      * and an empty filter for the user to populate (add)
      */
-    FeatureMatcherI noFilter = FeatureMatcher.byLabel(Condition.values()[0],
-            "");
-    filters.add(noFilter);
+    filters.add(FeatureMatcher.NULL_MATCHER);
+
+    /*
+     * use GridLayout to 'justify' rows to the top of the panel, until
+     * there are too many to fit in, then fall back on BoxLayout
+     */
+    if (filters.size() <= 5)
+    {
+      chooseFiltersPanel.setLayout(new GridLayout(5, 1));
+    }
+    else
+    {
+      chooseFiltersPanel.setLayout(
+              new BoxLayout(chooseFiltersPanel, BoxLayout.Y_AXIS));
+    }
 
     /*
      * render the conditions in rows, each in its own JPanel
@@ -1277,11 +1292,7 @@ public class FeatureTypeSettings extends JalviewDialog
     int filterIndex = 0;
     for (FeatureMatcherI filter : filters)
     {
-      String[] attName = filter.getKey();
-      Condition condition = filter.getMatcher().getCondition();
-      String pattern = filter.getMatcher().getPattern();
-      JPanel row = addFilter(attName, attNames, condition, pattern,
-              filterIndex);
+      JPanel row = addFilter(filter, attNames, filterIndex);
       row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
       chooseFiltersPanel.add(row);
       filterIndex++;
@@ -1294,25 +1305,36 @@ public class FeatureTypeSettings extends JalviewDialog
   /**
    * A helper method that constructs a row (panel) with one filter condition:
    * <ul>
-   * <li>a drop-down list of attribute names to choose from</li>
+   * <li>a drop-down list of Label, Score and attribute names to choose from</li>
    * <li>a drop-down list of conditions to choose from</li>
    * <li>a text field for input of a match pattern</li>
    * <li>optionally, a 'remove' button</li>
    * </ul>
-   * If attribute, condition or pattern are not null, they are set as defaults for
-   * the input fields. The 'remove' button is added unless the pattern is null or
-   * empty (incomplete filter condition).
+   * The filter values are set as defaults for the input fields. The 'remove'
+   * button is added unless the pattern is empty (incomplete filter condition).
+   * <p>
+   * Action handlers on these fields provide for
+   * <ul>
+   * <li>validate pattern field - should be numeric if condition is numeric</li>
+   * <li>save filters and refresh display on any (valid) change</li>
+   * <li>remove filter and refresh on 'Remove'</li>
+   * <li>update conditions list on change of Label/Score/Attribute</li>
+   * <li>refresh value field tooltip with min-max range on change of
+   * attribute</li>
+   * </ul>
    * 
-   * @param attName
+   * @param filter
    * @param attNames
-   * @param cond
-   * @param pattern
    * @param filterIndex
    * @return
    */
-  protected JPanel addFilter(String[] attName, List<String[]> attNames,
-          Condition cond, String pattern, int filterIndex)
+  protected JPanel addFilter(FeatureMatcherI filter,
+          List<String[]> attNames, int filterIndex)
   {
+    String[] attName = filter.getAttribute();
+    Condition cond = filter.getMatcher().getCondition();
+    String pattern = filter.getMatcher().getPattern();
+
     JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
     filterRow.setBackground(Color.white);
 
@@ -1322,8 +1344,12 @@ public class FeatureTypeSettings extends JalviewDialog
      */
     final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
             true, true);
+    String filterBy = setSelectedAttribute(attCombo, filter);
+
     JComboBox<Condition> condCombo = new JComboBox<>();
+
     JTextField patternField = new JTextField(8);
+    patternField.setText(pattern);
 
     /*
      * action handlers that validate and (if valid) apply changes
@@ -1351,7 +1377,7 @@ public class FeatureTypeSettings extends JalviewDialog
       }
     };
 
-    if (attName == null) // the 'add a condition' row
+    if (filter == FeatureMatcher.NULL_MATCHER) // the 'add a condition' row
     {
       attCombo.setSelectedIndex(0);
     }
@@ -1369,7 +1395,8 @@ public class FeatureTypeSettings extends JalviewDialog
          * ensure it is appropriate for the attribute datatype
          */
         populateConditions((String) attCombo.getSelectedItem(),
-                (Condition) condCombo.getSelectedItem(), condCombo);
+                (Condition) condCombo.getSelectedItem(), condCombo,
+                patternField);
         actionListener.actionPerformed(null);
       }
     });
@@ -1379,8 +1406,7 @@ public class FeatureTypeSettings extends JalviewDialog
     /*
      * drop-down choice of test condition
      */
-    populateConditions((String) attCombo.getSelectedItem(), cond,
-            condCombo);
+    populateConditions(filterBy, cond, condCombo, patternField);
     condCombo.setPreferredSize(new Dimension(150, 20));
     condCombo.addItemListener(itemListener);
     filterRow.add(condCombo);
@@ -1388,7 +1414,6 @@ public class FeatureTypeSettings extends JalviewDialog
     /*
      * pattern to match against
      */
-    patternField.setText(pattern);
     patternField.addActionListener(actionListener);
     patternField.addFocusListener(new FocusAdapter()
     {
@@ -1413,7 +1438,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * if a numeric condition is selected, show the value range
      * as a tooltip on the value input field
      */
-    updatePatternTooltip(attName, selectedCondition, patternField);
+    setPatternTooltip(filterBy, selectedCondition, patternField);
 
     /*
      * add remove button if filter is populated (non-empty pattern)
@@ -1441,6 +1466,33 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
+   * Sets the selected item in the Label/Score/Attribute drop-down to match the
+   * filter
+   * 
+   * @param attCombo
+   * @param filter
+   */
+  private String setSelectedAttribute(JComboBox<String> attCombo,
+          FeatureMatcherI filter)
+  {
+    String item = null;
+    if (filter.isByScore())
+    {
+      item = SCORE_18N;
+    }
+    else if (filter.isByLabel())
+    {
+      item = LABEL_18N;
+    }
+    else
+    {
+      item = toAttributeDisplayName(filter.getAttribute());
+    }
+    attCombo.setSelectedItem(item);
+    return item;
+  }
+
+  /**
    * If a numeric comparison condition is selected, retrieve the min-max range for
    * the value (score or attribute), and set it as a tooltip on the value file
    * 
@@ -1448,7 +1500,7 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param selectedCondition
    * @param patternField
    */
-  private void updatePatternTooltip(String[] attName,
+  private void setPatternTooltip(String attName,
           Condition selectedCondition, JTextField patternField)
   {
     patternField.setToolTipText("");
@@ -1469,22 +1521,24 @@ public class FeatureTypeSettings extends JalviewDialog
    * Populates the drop-down list of comparison conditions for the given attribute
    * name. The conditions added depend on the datatype of the attribute values.
    * The supplied condition is set as the selected item in the list, provided it
-   * is in the list.
+   * is in the list. If the pattern is now invalid (non-numeric pattern for a
+   * numeric condition), it is cleared.
    * 
    * @param attName
    * @param cond
    * @param condCombo
+   * @param patternField
    */
   private void populateConditions(String attName, Condition cond,
-          JComboBox<Condition> condCombo)
+          JComboBox<Condition> condCombo, JTextField patternField)
   {
     Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
             fromAttributeDisplayName(attName));
-    if (MessageManager.getString("label.label").equals(attName))
+    if (LABEL_18N.equals(attName))
     {
       type = Datatype.Character;
     }
-    else if (MessageManager.getString("label.score").equals(attName))
+    else if (SCORE_18N.equals(attName))
     {
       type = Datatype.Number;
     }
@@ -1522,6 +1576,24 @@ public class FeatureTypeSettings extends JalviewDialog
     }
 
     condCombo.addItemListener(listener);
+
+    /*
+     * clear pattern if it is now invalid for condition
+     */
+    if (((Condition) condCombo.getSelectedItem()).isNumeric())
+    {
+      try
+      {
+        String pattern = patternField.getText().trim();
+        if (pattern.length() > 0)
+        {
+          Float.valueOf(pattern);
+        }
+      } catch (NumberFormatException e)
+      {
+        patternField.setText("");
+      }
+    }
   }
 
   /**
@@ -1544,7 +1616,7 @@ public class FeatureTypeSettings extends JalviewDialog
     }
 
     Condition cond = (Condition) condCombo.getSelectedItem();
-    if (cond.needsAPattern())
+    if (!cond.needsAPattern())
     {
       return true;
     }
@@ -1598,8 +1670,7 @@ public class FeatureTypeSettings extends JalviewDialog
     Condition cond = (Condition) condCombo.getSelectedItem();
     String pattern = valueField.getText().trim();
 
-    updatePatternTooltip(fromAttributeDisplayName(attName), cond,
-            valueField);
+    setPatternTooltip(attName, cond, valueField);
 
     if (pattern.length() == 0 && cond.needsAPattern())
     {
@@ -1611,11 +1682,11 @@ public class FeatureTypeSettings extends JalviewDialog
      * or named attribute
      */
     FeatureMatcherI km = null;
-    if (MessageManager.getString("label.label").equals(attName))
+    if (LABEL_18N.equals(attName))
     {
       km = FeatureMatcher.byLabel(cond, pattern);
     }
-    else if (MessageManager.getString("label.score").equals(attName))
+    else if (SCORE_18N.equals(attName))
     {
       km = FeatureMatcher.byScore(cond, pattern);
     }
index 3401ae8..3047802 100644 (file)
@@ -2,9 +2,6 @@ package jalview.util.matcher;
 
 import jalview.util.MessageManager;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * An enumeration for binary conditions that a user might choose from when
  * setting filter or match conditions for values
@@ -15,8 +12,6 @@ public enum Condition
   NotMatches(false, true), Present(false, false), NotPresent(false, false),
   EQ(true, true), NE(true, true), LT(true, true), LE(true, true),
   GT(true, true), GE(true, true);
-
-  private static Map<Condition, String> displayNames = new HashMap<>();
   
   private boolean numeric;
 
@@ -60,14 +55,7 @@ public enum Condition
   @Override
   public String toString()
   {
-    String name = displayNames.get(this);
-    if (name != null)
-    {
-      return name;
-    }
-    name = MessageManager
-            .getStringOrReturn("label.matchCondition_", name());
-    displayNames.put(this, name);
-    return name;
+    return MessageManager.getStringOrReturn("label.matchCondition_",
+            name());
   }
 }
index 14a8585..353df83 100644 (file)
@@ -14,12 +14,17 @@ public class Matcher implements MatcherI
   Condition condition;
 
   /*
-   * the string value (upper-cased), or the regex, to compare to
+   * the string pattern as entered, or the regex, to compare to
    * also holds the string form of float value if a numeric condition
    */
   String pattern;
 
   /*
+   * the pattern in upper case, for non-case-sensitive matching
+   */
+  String uppercasePattern;
+
+  /*
    * the compiled regex if using a pattern match condition
    * (reserved for possible future enhancement)
    */
@@ -44,16 +49,21 @@ public class Matcher implements MatcherI
    */
   public Matcher(Condition cond, String compareTo)
   {
+    Objects.requireNonNull(cond);
     condition = cond;
     if (cond.isNumeric())
     {
       value = Float.valueOf(compareTo);
       pattern = String.valueOf(value);
+      uppercasePattern = pattern;
     }
     else
     {
-      // pattern matches will be non-case-sensitive
-      pattern = compareTo == null ? null : compareTo.toUpperCase();
+      pattern = compareTo;
+      if (pattern != null)
+      {
+        uppercasePattern = pattern.toUpperCase();
+      }
     }
 
     // if we add regex conditions (e.g. matchesPattern), then
@@ -71,10 +81,7 @@ public class Matcher implements MatcherI
    */
   public Matcher(Condition cond, float compareTo)
   {
-    Objects.requireNonNull(cond);
-    condition = cond;
-    value = compareTo;
-    pattern = String.valueOf(compareTo).toUpperCase();
+    this(cond, String.valueOf(compareTo));
   }
 
   /**
@@ -113,16 +120,16 @@ public class Matcher implements MatcherI
     boolean matched = false;
     switch(condition) {
     case Matches:
-      matched = upper.equals(pattern);
+      matched = upper.equals(uppercasePattern);
       break;
     case NotMatches:
-      matched = !upper.equals(pattern);
+      matched = !upper.equals(uppercasePattern);
       break;
     case Contains:
-      matched = upper.indexOf(pattern) > -1;
+      matched = upper.indexOf(uppercasePattern) > -1;
       break;
     case NotContains:
-      matched = upper.indexOf(pattern) == -1;
+      matched = upper.indexOf(uppercasePattern) == -1;
       break;
     case Present:
       matched = true;
@@ -186,7 +193,7 @@ public class Matcher implements MatcherI
 
   /**
    * equals is overridden so that we can safely remove Matcher objects from
-   * collections (e.g. delete an attribut match condition for a feature colour)
+   * collections (e.g. delete an attribute match condition for a feature colour)
    */
   @Override
   public boolean equals(Object obj)
@@ -196,8 +203,15 @@ public class Matcher implements MatcherI
       return false;
     }
     Matcher m = (Matcher) obj;
-    return condition == m.condition && value == m.value
-            && pattern.equals(m.pattern);
+    if (condition != m.condition || value != m.value)
+    {
+      return false;
+    }
+    if (pattern == null)
+    {
+      return m.pattern == null;
+    }
+    return uppercasePattern.equals(m.uppercasePattern);
   }
 
   @Override
@@ -222,7 +236,7 @@ public class Matcher implements MatcherI
   public String toString()
   {
     StringBuilder sb = new StringBuilder();
-    sb.append(condition.name()).append(" ");
+    sb.append(condition.toString()).append(" ");
     if (condition.isNumeric())
     {
       sb.append(pattern);
index a98013b..56644fd 100644 (file)
@@ -11,6 +11,7 @@ import jalview.util.matcher.Condition;
 
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Locale;
 import java.util.Map;
 
 import org.testng.annotations.Test;
@@ -111,14 +112,14 @@ public class FeatureMatcherSetTest
   @Test(groups = "Functional")
   public void testToString()
   {
+    Locale.setDefault(Locale.ENGLISH);
     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.LT, "1.2",
             "AF");
     assertEquals(fm1.toString(), "AF < 1.2");
 
     FeatureMatcher fm2 = FeatureMatcher.byAttribute(Condition.NotContains,
-            "path",
-            "CLIN_SIG");
-    assertEquals(fm2.toString(), "CLIN_SIG Does not contain 'PATH'");
+            "path", "CLIN_SIG");
+    assertEquals(fm2.toString(), "CLIN_SIG does not contain 'path'");
 
     /*
      * AND them
@@ -129,7 +130,7 @@ public class FeatureMatcherSetTest
     assertEquals(fms.toString(), "(AF < 1.2)");
     fms.and(fm2);
     assertEquals(fms.toString(),
-            "(AF < 1.2) AND (CLIN_SIG Does not contain 'PATH')");
+            "(AF < 1.2) and (CLIN_SIG does not contain 'path')");
 
     /*
      * OR them
@@ -140,7 +141,7 @@ public class FeatureMatcherSetTest
     assertEquals(fms.toString(), "(AF < 1.2)");
     fms.or(fm2);
     assertEquals(fms.toString(),
-            "(AF < 1.2) OR (CLIN_SIG Does not contain 'PATH')");
+            "(AF < 1.2) or (CLIN_SIG does not contain 'path')");
 
     try
     {
@@ -265,7 +266,7 @@ public class FeatureMatcherSetTest
     assertFalse(fms.matches(sf));
     csq.put("Consequence", "junk");
     assertFalse(fms.matches(sf));
-  
+
     /*
      * a string pattern matcher
      */
index f4e9351..62b03a3 100644 (file)
@@ -2,11 +2,15 @@ package jalview.datamodel.features;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
 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;
 
 public class FeatureMatcherTest
@@ -126,6 +130,8 @@ public class FeatureMatcherTest
   @Test
   public void testToString()
   {
+    Locale.setDefault(Locale.ENGLISH);
+
     /*
      * toString uses the i18n translation of the enum conditions
      */
@@ -137,24 +143,65 @@ public class FeatureMatcherTest
      * Present / NotPresent omit the value pattern
      */
     fm = FeatureMatcher.byAttribute(Condition.Present, "", "AF");
-    assertEquals(fm.toString(), "AF Is present");
+    assertEquals(fm.toString(), "AF is present");
     fm = FeatureMatcher.byAttribute(Condition.NotPresent, "", "AF");
-    assertEquals(fm.toString(), "AF Is not present");
+    assertEquals(fm.toString(), "AF is not present");
+
+    /*
+     * by Label
+     */
+    fm = FeatureMatcher.byLabel(Condition.Matches, "foobar");
+    assertEquals(fm.toString(),
+            MessageManager.getString("label.label") + " matches 'foobar'");
+
+    /*
+     * by Score
+     */
+    fm = FeatureMatcher.byScore(Condition.GE, "12.2");
+    assertEquals(fm.toString(),
+            MessageManager.getString("label.score") + " >= 12.2");
   }
 
   @Test
-  public void testGetKey()
+  public void testGetAttribute()
   {
     FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2",
             "AF");
-    assertEquals(fm.getKey(), new String[] { "AF" });
+    assertEquals(fm.getAttribute(), new String[] { "AF" });
 
     /*
      * compound key (attribute / subattribute)
      */
     fm = FeatureMatcher.byAttribute(Condition.GE, "-2F", "CSQ",
             "Consequence");
-    assertEquals(fm.getKey(), new String[] { "CSQ", "Consequence" });
+    assertEquals(fm.getAttribute(), new String[] { "CSQ", "Consequence" });
+
+    /*
+     * answers null if match is by Label or by Score
+     */
+    assertNull(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+            .getAttribute());
+    assertNull(FeatureMatcher.byScore(Condition.LE, "-1").getAttribute());
+  }
+
+  @Test
+  public void testIsByLabel()
+  {
+    assertTrue(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+            .isByLabel());
+    assertFalse(FeatureMatcher.byScore(Condition.LE, "-1").isByLabel());
+    assertFalse(FeatureMatcher.byAttribute(Condition.LE, "-1", "AC")
+            .isByLabel());
+  }
+
+  @Test
+  public void testIsByScore()
+  {
+    assertFalse(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+            .isByScore());
+    assertTrue(FeatureMatcher.byScore(Condition.LE, "-1").isByScore());
+    assertFalse(FeatureMatcher.byAttribute(Condition.LE, "-1", "AC")
+            .isByScore());
   }
 
   @Test
index f4c8181..a47fb60 100644 (file)
@@ -6,8 +6,12 @@ import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+import java.util.Locale;
+
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 public class MatcherTest
 {
   @Test(groups = "Functional")
@@ -15,7 +19,8 @@ public class MatcherTest
   {
     MatcherI m = new Matcher(Condition.Contains, "foo");
     assertEquals(m.getCondition(), Condition.Contains);
-    assertEquals(m.getPattern(), "FOO"); // all comparisons upper-cased
+    assertEquals(m.getPattern(), "foo");
+    assertEquals(PA.getValue(m, "uppercasePattern"), "FOO");
     assertEquals(m.getFloatValue(), 0f);
 
     m = new Matcher(Condition.GT, -2.1f);
@@ -25,7 +30,7 @@ public class MatcherTest
 
     m = new Matcher(Condition.NotContains, "-1.2f");
     assertEquals(m.getCondition(), Condition.NotContains);
-    assertEquals(m.getPattern(), "-1.2F");
+    assertEquals(m.getPattern(), "-1.2f");
     assertEquals(m.getFloatValue(), 0f);
 
     m = new Matcher(Condition.GE, "-1.2f");
@@ -208,11 +213,13 @@ public class MatcherTest
   @Test(groups = "Functional")
   public void testToString()
   {
+    Locale.setDefault(Locale.ENGLISH);
+
     MatcherI m = new Matcher(Condition.LT, 1.2e-6f);
-    assertEquals(m.toString(), "LT 1.2E-6");
+    assertEquals(m.toString(), "< 1.2E-6");
 
     m = new Matcher(Condition.NotMatches, "ABC");
-    assertEquals(m.toString(), "NotMatches 'ABC'");
+    assertEquals(m.toString(), "Does not match 'ABC'");
 
     m = new Matcher(Condition.Contains, -1.2f);
     assertEquals(m.toString(), "Contains '-1.2'");