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 = <
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 = <
*/
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;
{
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);
}
*/
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;
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;
{
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;
*/
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());
// ///////////////////////////////////////////////////////////////////////
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"),
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;
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
*/
* and an empty filter for the user to populate (add)
*/
KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
- (String) null);
+ (String[]) null);
filters.add(noFilter);
/*
{
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);
*/
populateConditions((String) attCombo.getSelectedItem(), cond,
condCombo);
+ condCombo.setPreferredSize(new Dimension(150, 20));
condCombo.addItemListener(itemListener);
filterRow.add(condCombo);
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)
JComboBox<Condition> condCombo)
{
Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
- attName);
+ fromAttributeDisplayName(attName));
if (MessageManager.getString("label.label").equals(attName))
{
type = Datatype.Character;
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
}
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();
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)
{
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;
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";
* 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)
{
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<>();
}
/**
- * 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();
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
if (val == null)
{
return condition == Condition.NotContains
- || condition == Condition.NotMatches;
+ || condition == Condition.NotMatches
+ || condition == Condition.NotPresent;
}
String upper = val.toUpperCase().trim();
case NotContains:
matched = upper.indexOf(pattern) == -1;
break;
+ case Present:
+ matched = true;
+ break;
+ default:
+ break;
}
return matched;
}
case GE:
matched = f >= value;
break;
+ default:
+ break;
}
return matched;
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
* 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
{
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);
}
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;
* - 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);
/*
* ..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);
/*
* 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);
/*
* 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);
* 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));
* 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));
/*
* 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());
/*
* 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);
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;
* 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"));
/*
* 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);
/*
* 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);
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(), ">");
*/
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
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");