JAL-2808 update spike to latest (filter range, Present option)
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 23 Nov 2017 12:52:45 +0000 (12:52 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 23 Nov 2017 12:52:45 +0000 (12:52 +0000)
15 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/FeatureSettings.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/io/vcf/VCFLoader.java
src/jalview/util/matcher/Condition.java
src/jalview/util/matcher/KeyedMatcher.java
src/jalview/util/matcher/Matcher.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
test/jalview/renderer/seqfeatures/FeatureRendererTest.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 39a2747..ad04171 100755 (executable)
  */
 package jalview.appletgui;
 
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
-
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
 import java.awt.BorderLayout;
 import java.awt.Button;
@@ -587,27 +584,19 @@ public class FeatureSettings extends Panel
   {
     Component[] comps = featurePanel.getComponents();
     int cSize = comps.length;
-
-    /*
-     * temporary! leave column[2] empty - used for Filter in
-     * gui.FeatureSettings
-     */
-    int columnCount = 4;
-    Object[][] tmp = new Object[cSize][columnCount];
-    int tmpSize = 0;
-    for (int i = 0; i < cSize; i++)
-    {
-      MyCheckbox check = (MyCheckbox) comps[i];
-      tmp[tmpSize][TYPE_COLUMN /* 0 */] = check.type;
-      tmp[tmpSize][COLOUR_COLUMN /* 1 */] = fr.getFeatureStyle(check.type);
-      tmp[tmpSize][SHOW_COLUMN /* 3 */] = new Boolean(check.getState());
-      tmpSize++;
+    FeatureSettingsBean[] rowData = new FeatureSettingsBean[cSize];
+    int i = 0;
+    for (Component comp : comps)
+    {
+      MyCheckbox check = (MyCheckbox) comp;
+      // feature filter set to null as not (yet) offered in applet
+      FeatureColourI colour = fr.getFeatureStyle(check.type);
+      rowData[i] = new FeatureSettingsBean(check.type, colour, null,
+              check.getState());
+      i++;
     }
 
-    Object[][] data = new Object[tmpSize][columnCount];
-    System.arraycopy(tmp, 0, data, 0, tmpSize);
-
-    fr.setFeaturePriority(data);
+    fr.setFeaturePriority(rowData);
 
     ap.paintAlignment(updateOverview, updateOverview);
   }
index fedfe3f..2b3688f 100644 (file)
  */
 package jalview.gui;
 
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.FILTER_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
+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;
@@ -42,6 +42,7 @@ import jalview.util.QuickSort;
 import jalview.util.matcher.KeyedMatcherSet;
 import jalview.util.matcher.KeyedMatcherSetI;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 import jalview.ws.DasSequenceFeatureFetcher;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 
@@ -111,14 +112,10 @@ public class FeatureSettings extends JPanel
 {
   private static final int COLUMN_COUNT = 4;
 
-  private static final String COLON = ":";
-
   private static final int MIN_WIDTH = 400;
 
   private static final int MIN_HEIGHT = 400;
 
-  private static final int MAX_TOOLTIP_LENGTH = 50;
-
   DasSourceBrowser dassourceBrowser;
 
   DasSequenceFeatureFetcher dasFeatureFetcher;
@@ -1105,12 +1102,32 @@ public class FeatureSettings extends JPanel
    */
   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
   {
-    if (fr.setFeaturePriority(data, visibleNew))
+    FeatureSettingsBean[] rowData = getTableAsBeans(data);
+
+    if (fr.setFeaturePriority(rowData, visibleNew))
     {
       af.alignPanel.paintAlignment(true, true);
     }
   }
 
+  /**
+   * Converts table data into an array of data beans
+   */
+  private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
+  {
+    FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
+    for (int i = 0; i < data.length; i++)
+    {
+      String type = (String) data[i][TYPE_COLUMN];
+      FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
+      KeyedMatcherSetI theFilter = (KeyedMatcherSetI) data[i][FILTER_COLUMN];
+      Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
+      rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
+              isShown);
+    }
+    return rowData;
+  }
+
   private void jbInit() throws Exception
   {
     this.setLayout(new BorderLayout());
@@ -1492,6 +1509,16 @@ 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"),
index e280091..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);
 
     /*
@@ -1330,7 +1334,20 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       attCombo.setSelectedItem(toAttributeDisplayName(attName));
     }
-    attCombo.addItemListener(itemListener);
+    attCombo.addItemListener(new ItemListener()
+    {
+      @Override
+      public void itemStateChanged(ItemEvent e)
+      {
+        /*
+         * on change of attribute, refresh the conditions list to
+         * ensure it is appropriate for the attribute datatype
+         */
+        populateConditions((String) attCombo.getSelectedItem(),
+                (Condition) condCombo.getSelectedItem(), condCombo);
+        actionListener.actionPerformed(null);
+      }
+    });
 
     filterRow.add(attCombo);
 
@@ -1339,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);
 
@@ -1358,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)
@@ -1395,7 +1439,7 @@ public class FeatureTypeSettings extends JalviewDialog
           JComboBox<Condition> condCombo)
   {
     Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
-            attName);
+            fromAttributeDisplayName(attName));
     if (MessageManager.getString("label.label").equals(attName))
     {
       type = Datatype.Character;
@@ -1405,30 +1449,47 @@ public class FeatureTypeSettings extends JalviewDialog
       type = Datatype.Number;
     }
 
+    /*
+     * remove itemListener before starting
+     */
+    ItemListener listener = condCombo.getItemListeners()[0];
+    condCombo.removeItemListener(listener);
+    boolean condIsValid = false;
+    condCombo.removeAllItems();
     for (Condition c : Condition.values())
     {
       if ((c.isNumeric() && type != Datatype.Character)
               || (!c.isNumeric() && type != Datatype.Number))
       {
         condCombo.addItem(c);
+        if (c == cond)
+        {
+          condIsValid = true;
+        }
       }
     }
 
     /*
      * set the selected condition (does nothing if not in the list)
      */
-    if (cond != null)
+    if (condIsValid)
     {
       condCombo.setSelectedItem(cond);
     }
+    else
+    {
+      condCombo.setSelectedIndex(0);
+    }
+
+    condCombo.addItemListener(listener);
   }
 
   /**
    * 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
@@ -1443,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();
@@ -1523,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 2847bd7..155e773 100644 (file)
@@ -27,6 +27,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
@@ -55,7 +57,7 @@ public class VCFLoader
 
   private static final String VCF_FIELDS_PREF = "VCF_FIELDS";
 
-  private static final String DEFAULT_VCF_FIELDS = "AF,AC*";
+  private static final String DEFAULT_VCF_FIELDS = ".*";
 
   private static final String DEFAULT_VEP_FIELDS = ".*";// "Allele,Consequence,IMPACT,SWISSPROT,SIFT,PolyPhen,CLIN_SIG";
 
@@ -915,7 +917,8 @@ public class VCFLoader
      * inspect CSQ consequences; where possible restrict to the consequence
      * associated with the current transcript (Feature)
      */
-    Map<String, String> csqValues = new HashMap<>();
+    SortedMap<String, String> csqValues = new TreeMap<>(
+            String.CASE_INSENSITIVE_ORDER);
 
     for (String consequence : consequences)
     {
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 4797675..8bdcad4 100644 (file)
@@ -50,16 +50,27 @@ public abstract class FeatureRendererModel
         implements jalview.api.FeatureRenderer
 {
   /*
-   * column indices of fields in Feature Settings table
-   * todo: transfer valuers as data beans instead of Object[][]
+   * a data bean to hold one row of feature settings from the gui
    */
-  public static final int TYPE_COLUMN = 0;
+  public static class FeatureSettingsBean
+  {
+    public final String featureType;
+
+    public final FeatureColourI featureColour;
 
-  public static final int COLOUR_COLUMN = 1;
+    public final KeyedMatcherSetI filter;
 
-  public static final int FILTER_COLUMN = 2;
+    public final Boolean show;
 
-  public static final int SHOW_COLUMN = 3;
+    public FeatureSettingsBean(String type, FeatureColourI colour,
+            KeyedMatcherSetI theFilter, Boolean isShown)
+    {
+      featureType = type;
+      featureColour = colour;
+      filter = theFilter;
+      show = isShown;
+    }
+  }
 
   /*
    * global transparency for feature
@@ -673,26 +684,27 @@ public abstract class FeatureRendererModel
    * Replace current ordering with new ordering
    * 
    * @param data
-   *          { String(Type), Colour(Type), Boolean(Displayed) }
+   *          an array of { Type, Colour, Filter, Boolean }
    * @return true if any visible features have been reordered, else false
    */
-  public boolean setFeaturePriority(Object[][] data)
+  public boolean setFeaturePriority(FeatureSettingsBean[] data)
   {
     return setFeaturePriority(data, true);
   }
 
   /**
-   * Sets the priority order for features, with the highest priority (displayed
-   * on top) at the start of the data array
+   * Sets the priority order for features, with the highest priority (displayed on
+   * top) at the start of the data array
    * 
    * @param data
-   *          { String(Type), Colour(Type), Boolean(Displayed) }
+   *          an array of { Type, Colour, Filter, Boolean }
    * @param visibleNew
    *          when true current featureDisplay list will be cleared
-   * @return true if any visible features have been reordered or recoloured,
-   *         else false (i.e. no need to repaint)
+   * @return true if any visible features have been reordered or recoloured, else
+   *         false (i.e. no need to repaint)
    */
-  public boolean setFeaturePriority(Object[][] data, boolean visibleNew)
+  public boolean setFeaturePriority(FeatureSettingsBean[] data,
+          boolean visibleNew)
   {
     /*
      * note visible feature ordering and colours before update
@@ -731,9 +743,9 @@ public abstract class FeatureRendererModel
     {
       for (int i = 0; i < data.length; i++)
       {
-        String type = data[i][TYPE_COLUMN].toString();
-        setColour(type, (FeatureColourI) data[i][COLOUR_COLUMN]);
-        if (((Boolean) data[i][SHOW_COLUMN]).booleanValue())
+        String type = data[i].featureType;
+        setColour(type, data[i].featureColour);
+        if (data[i].show)
         {
           av_featuresdisplayed.setVisible(type);
         }
index f6dfed6..d8b905e 100644 (file)
@@ -15,6 +15,7 @@ import jalview.gui.FeatureRenderer;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.schemes.FeatureColour;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
 import java.awt.Color;
 import java.util.List;
@@ -172,9 +173,9 @@ public class FeatureColourFinderTest
      * - currently no way other than mimicking reordering of
      * table in Feature Settings
      */
-    Object[][] data = new Object[2][];
-    data[0] = new Object[] { "Metal", red, true };
-    data[1] = new Object[] { "Domain", green, true };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[2];
+    data[0] = new FeatureSettingsBean("Metal", red, null, true);
+    data[1] = new FeatureSettingsBean("Domain", green, null, true);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.blue, seq, 10);
     assertEquals(c, Color.red);
@@ -182,7 +183,7 @@ public class FeatureColourFinderTest
     /*
      * ..and turn off display of Metal
      */
-    data[0][2] = false;
+    data[0] = new FeatureSettingsBean("Metal", red, null, false);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.blue, seq, 10);
     assertEquals(c, Color.green);
@@ -216,8 +217,8 @@ public class FeatureColourFinderTest
     /*
      * turn off display of Metal - is this the easiest way to do it??
      */
-    Object[][] data = new Object[1][];
-    data[0] = new Object[] { "Metal", red, false };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[1];
+    data[0] = new FeatureSettingsBean("Metal", red, null, false);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.blue, seq, 10);
     assertEquals(c, Color.blue);
@@ -225,7 +226,7 @@ public class FeatureColourFinderTest
     /*
      * turn display of Metal back on
      */
-    data[0] = new Object[] { "Metal", red, true };
+    data[0] = new FeatureSettingsBean("Metal", red, null, true);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.blue, seq, 10);
     assertEquals(c, Color.red);
@@ -399,9 +400,9 @@ public class FeatureColourFinderTest
      * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
      * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
      */
-    Object[][] data = new Object[2][];
-    data[0] = new Object[] { "Metal", red, true };
-    data[1] = new Object[] { "Domain", green, true };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[2];
+    data[0] = new FeatureSettingsBean("Metal", red, null, true);
+    data[1] = new FeatureSettingsBean("Domain", green, null, true);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.cyan, seq, 10);
     assertEquals(c, new Color(153, 102, 41));
@@ -411,7 +412,7 @@ public class FeatureColourFinderTest
      * Domain (green) above background (pink)
      * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
      */
-    data[0][2] = false;
+    data[0] = new FeatureSettingsBean("Metal", red, null, false);
     fr.setFeaturePriority(data);
     c = finder.findFeatureColour(Color.pink, seq, 10);
     assertEquals(c, new Color(102, 223, 70));
@@ -447,8 +448,8 @@ public class FeatureColourFinderTest
     /*
      * turn off display of Metal
      */
-    Object[][] data = new Object[1][];
-    data[0] = new Object[] { "Metal", red, false };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[1];
+    data[0] = new FeatureSettingsBean("Metal", red, null, false);
     fr.setFeaturePriority(data);
     assertTrue(finder.noFeaturesDisplayed());
 
@@ -503,9 +504,9 @@ public class FeatureColourFinderTest
     /*
      * render order is kd above Metal
      */
-    Object[][] data = new Object[2][];
-    data[0] = new Object[] { kdFeature, fc, true };
-    data[1] = new Object[] { metalFeature, green, true };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[2];
+    data[0] = new FeatureSettingsBean(kdFeature, fc, null, true);
+    data[1] = new FeatureSettingsBean(metalFeature, green, null, true);
     fr.setFeaturePriority(data);
 
     av.setShowSequenceFeatures(true);
index 745eec3..73ae9d7 100644 (file)
@@ -17,6 +17,7 @@ import jalview.util.matcher.Condition;
 import jalview.util.matcher.KeyedMatcher;
 import jalview.util.matcher.KeyedMatcherSet;
 import jalview.util.matcher.KeyedMatcherSetI;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
 import java.awt.Color;
 import java.util.ArrayList;
@@ -122,11 +123,11 @@ public class FeatureRendererTest
      * change render order (todo: an easier way)
      * nb here last comes first in the data array
      */
-    Object[][] data = new Object[3][];
+    FeatureSettingsBean[] data = new FeatureSettingsBean[3];
     FeatureColourI colour = new FeatureColour(Color.RED);
-    data[0] = new Object[] { "Rfam", colour, true };
-    data[1] = new Object[] { "Pfam", colour, false };
-    data[2] = new Object[] { "Scop", colour, false };
+    data[0] = new FeatureSettingsBean("Rfam", colour, null, true);
+    data[1] = new FeatureSettingsBean("Pfam", colour, null, false);
+    data[2] = new FeatureSettingsBean("Scop", colour, null, false);
     fr.setFeaturePriority(data);
     assertEquals(fr.getRenderOrder(), Arrays.asList("Scop", "Pfam", "Rfam"));
     assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
@@ -223,12 +224,13 @@ public class FeatureRendererTest
     /*
      * make "Type2" not displayed
      */
-    Object[][] data = new Object[4][];
     FeatureColourI colour = new FeatureColour(Color.RED);
-    data[0] = new Object[] { "Type1", colour, true };
-    data[1] = new Object[] { "Type2", colour, false };
-    data[2] = new Object[] { "Type3", colour, true };
-    data[3] = new Object[] { "Disulphide Bond", colour, true };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[4];
+    data[0] = new FeatureSettingsBean("Type1", colour, null, true);
+    data[1] = new FeatureSettingsBean("Type2", colour, null, false);
+    data[2] = new FeatureSettingsBean("Type3", colour, null, true);
+    data[3] = new FeatureSettingsBean("Disulphide Bond", colour, null,
+            true);
     fr.setFeaturePriority(data);
 
     features = fr.findFeaturesAtColumn(seq, 15);
@@ -377,11 +379,11 @@ public class FeatureRendererTest
     /*
      * hide feature type, then unhide
      */
-    Object[][] data = new Object[1][];
-    data[0] = new Object[] { "Cath", fc, false };
+    FeatureSettingsBean[] data = new FeatureSettingsBean[1];
+    data[0] = new FeatureSettingsBean("Cath", fc, null, false);
     fr.setFeaturePriority(data);
     assertNull(fr.getColour(sf1));
-    data[0] = new Object[] { "Cath", fc, true };
+    data[0] = new FeatureSettingsBean("Cath", fc, null, true);
     fr.setFeaturePriority(data);
     assertEquals(fr.getColour(sf1), Color.red);
 
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");