From 8ace2707dd1c5cfbb1688efa265fd0afcec35def Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 15 Nov 2017 10:42:49 +0000 Subject: [PATCH] JAL-2835 support filter/colour by nested attribute names --- src/jalview/api/FeatureColourI.java | 11 ++- src/jalview/api/FeatureRenderer.java | 10 +- src/jalview/datamodel/SequenceFeature.java | 60 ++++++------ .../datamodel/features/FeatureAttributes.java | 99 +++++++++++++++----- src/jalview/gui/FeatureColourChooser.java | 49 ++++++---- src/jalview/gui/FeatureSettings.java | 40 ++++---- src/jalview/io/SequenceAnnotationReport.java | 7 +- src/jalview/schemes/FeatureColour.java | 26 ++--- .../datamodel/features/FeatureAttributesTest.java | 41 ++++++++ 9 files changed, 230 insertions(+), 113 deletions(-) create mode 100644 test/jalview/datamodel/features/FeatureAttributesTest.java diff --git a/src/jalview/api/FeatureColourI.java b/src/jalview/api/FeatureColourI.java index 3eebf6c..93773cc 100644 --- a/src/jalview/api/FeatureColourI.java +++ b/src/jalview/api/FeatureColourI.java @@ -189,17 +189,18 @@ public interface FeatureColourI boolean isColourByAttribute(); /** - * Answers the name of the attribute used for colouring if any, or null + * Answers the name of the attribute (and optional sub-attribute...) used for + * colouring if any, or null * * @return */ - String getAttributeName(); + String[] getAttributeName(); /** - * Sets the name of the attribute used for colouring if any, or null to remove - * this property + * Sets the name of the attribute (and optional sub-attribute...) used for + * colouring if any, or null to remove this property * * @return */ - void setAttributeName(String name); + void setAttributeName(String... name); } diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java index 40c7d4d..ef0abbd 100644 --- a/src/jalview/api/FeatureRenderer.java +++ b/src/jalview/api/FeatureRenderer.java @@ -133,7 +133,7 @@ public interface FeatureRenderer List getGroups(boolean visible); /** - * change visibility for a range of groups + * Set visibility for a list of groups * * @param toset * @param visible @@ -141,7 +141,7 @@ public interface FeatureRenderer void setGroupVisibility(List toset, boolean visible); /** - * change visibiilty of given group + * Set visibility of the given feature group * * @param group * @param visible @@ -149,9 +149,9 @@ public interface FeatureRenderer void setGroupVisibility(String group, boolean visible); /** - * Returns features at the specified aligned column on the given sequence. - * Non-positional features are not included. If the column has a gap, then - * enclosing features are included (but not contact features). + * Returns visible features at the specified aligned column on the given + * sequence. Non-positional features are not included. If the column has a gap, + * then enclosing features are included (but not contact features). * * @param sequence * @param column diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index 2110632..8a6cb61 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -59,18 +59,6 @@ public class SequenceFeature implements FeatureLocationI private static final String ROW_DATA = "%s%s%s"; /* - * map of otherDetails special keys, and their value fields' delimiter - */ - private static final Map INFO_KEYS = new HashMap<>(); - - static - { - INFO_KEYS.put("CSQ", ","); - // todo capture second level metadata (CSQ FORMAT) - // and delimiter "|" so as to report in a table within a table? - } - - /* * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as * name1=value1;name2=value2,value3;...etc */ @@ -184,7 +172,7 @@ public class SequenceFeature implements FeatureLocationI if (sf.otherDetails != null) { - otherDetails = new HashMap(); + otherDetails = new HashMap<>(); for (Entry entry : sf.otherDetails.entrySet()) { otherDetails.put(entry.getKey(), entry.getValue()); @@ -192,7 +180,7 @@ public class SequenceFeature implements FeatureLocationI } if (sf.links != null && sf.links.size() > 0) { - links = new Vector(); + links = new Vector<>(); for (int i = 0, iSize = sf.links.size(); i < iSize; i++) { links.addElement(sf.links.elementAt(i)); @@ -359,7 +347,7 @@ public class SequenceFeature implements FeatureLocationI { if (links == null) { - links = new Vector(); + links = new Vector<>(); } if (!links.contains(labelLink)) @@ -394,18 +382,25 @@ public class SequenceFeature implements FeatureLocationI /** * Answers the value of the specified attribute as string, or null if no such - * value + * value. If more than one attribute name is provided, tries to resolve as keys + * to nested maps. For example, if attribute "CSQ" holds a map of key-value + * pairs, then getValueAsString("CSQ", "Allele") returns the value of "Allele" + * in that map. * * @param key * @return */ - public String getValueAsString(String key) + public String getValueAsString(String... key) { if (otherDetails == null) { return null; } - Object value = otherDetails.get(key); + Object value = otherDetails.get(key[0]); + if (key.length > 1 && value instanceof Map) + { + value = ((Map) value).get(key[1]); + } return value == null ? null : value.toString(); } @@ -438,7 +433,7 @@ public class SequenceFeature implements FeatureLocationI { if (otherDetails == null) { - otherDetails = new HashMap(); + otherDetails = new HashMap<>(); } otherDetails.put(key, value); @@ -463,8 +458,8 @@ public class SequenceFeature implements FeatureLocationI .getAttributeName(key); } - FeatureAttributes.getInstance().addAttribute(this.type, key, attDesc, - value.toString()); + FeatureAttributes.getInstance().addAttribute(this.type, attDesc, value, + key); } /* @@ -642,30 +637,33 @@ public class SequenceFeature implements FeatureLocationI { continue; // to avoid double reporting } - if (INFO_KEYS.containsKey(key)) + + Object value = entry.getValue(); + if (value instanceof Map) { /* - * split selected INFO data by delimiter over multiple lines + * expand values in a Map attribute across separate lines */ - String delimiter = INFO_KEYS.get(key); - String[] values = entry.getValue().toString().split(delimiter); - for (String value : values) + Map values = (Map) value; + for (Entry e : values.entrySet()) { - sb.append(String.format(ROW_DATA, key, "", value)); + sb.append(String.format(ROW_DATA, key, e.getKey().toString(), e + .getValue().toString())); } } else - { // tried but it failed to provide a tooltip :-( + { + // tried but it failed to provide a tooltip :-( String attDesc = null; if (metadata != null) { attDesc = metadata.getAttributeName(key); } - String value = entry.getValue().toString(); - if (isValueInteresting(key, value, metadata)) + String s = entry.getValue().toString(); + if (isValueInteresting(key, s, metadata)) { sb.append(String.format(ROW_DATA, key, attDesc == null ? "" - : attDesc, value)); + : attDesc, s)); } } } diff --git a/src/jalview/datamodel/features/FeatureAttributes.java b/src/jalview/datamodel/features/FeatureAttributes.java index 3dc4f19..7221d62 100644 --- a/src/jalview/datamodel/features/FeatureAttributes.java +++ b/src/jalview/datamodel/features/FeatureAttributes.java @@ -2,9 +2,11 @@ package jalview.datamodel.features; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeMap; /** @@ -14,7 +16,45 @@ public class FeatureAttributes { private static FeatureAttributes instance = new FeatureAttributes(); - private Map> attributes; + /* + * map, by feature type, of a map, by attribute name, of + * attribute description and min-max range (if known) + */ + private Map> attributes; + + /* + * a case-insensitive comparator so that attributes are ordered e.g. + * AC + * af + * CSQ:AFR_MAF + * CSQ:Allele + */ + private Comparator comparator = new Comparator() + { + @Override + public int compare(String[] o1, String[] o2) + { + int i = 0; + while (i < o1.length || i < o2.length) + { + if (o2.length <= i) + { + return o1.length <= i ? 0 : 1; + } + if (o1.length <= i) + { + return -1; + } + int comp = String.CASE_INSENSITIVE_ORDER.compare(o1[i], o2[i]); + if (comp != 0) + { + return comp; + } + i++; + } + return 0; // same length and all matched + } + }; private class AttributeData { @@ -116,17 +156,19 @@ public class FeatureAttributes } /** - * Answers the attributes known for the given feature type, in alphabetical - * order (not case sensitive), or an empty set if no attributes are known + * Answers the attribute names known for the given feature type, in + * alphabetical order (not case sensitive), or an empty set if no attributes + * are known. An attribute name is typically 'simple' e.g. "AC", but may be + * 'compound' e.g. {"CSQ", "Allele"} where a feature has map-valued attributes * * @param featureType * @return */ - public List getAttributes(String featureType) + public List getAttributes(String featureType) { if (!attributes.containsKey(featureType)) { - return Collections. emptyList(); + return Collections. emptyList(); } return new ArrayList<>(attributes.get(featureType).keySet()); @@ -156,23 +198,39 @@ public class FeatureAttributes * type, and updates the min-max for any numeric value * * @param featureType - * @param attName * @param description * @param value + * @param attName */ - public void addAttribute(String featureType, String attName, - String description, String value) + public void addAttribute(String featureType, String description, + Object value, String... attName) { if (featureType == null || attName == null) { return; } - Map atts = attributes.get(featureType); + /* + * if attribute value is a map, drill down one more level to + * record its sub-fields + */ + if (value instanceof Map) + { + for (Entry entry : ((Map) value).entrySet()) + { + String[] attNames = new String[attName.length + 1]; + System.arraycopy(attName, 0, attNames, 0, attName.length); + attNames[attName.length] = entry.getKey().toString(); + addAttribute(featureType, description, entry.getValue(), attNames); + } + return; + } + + String valueAsString = value.toString(); + Map atts = attributes.get(featureType); if (atts == null) { - atts = new TreeMap( - String.CASE_INSENSITIVE_ORDER); + atts = new TreeMap<>(comparator); attributes.put(featureType, atts); } AttributeData attData = atts.get(attName); @@ -181,7 +239,7 @@ public class FeatureAttributes attData = new AttributeData(); atts.put(attName, attData); } - attData.addInstance(description, value); + attData.addInstance(description, valueAsString); } /** @@ -192,10 +250,10 @@ public class FeatureAttributes * @param attName * @return */ - public String getDescription(String featureType, String attName) + public String getDescription(String featureType, String... attName) { String desc = null; - Map atts = attributes.get(featureType); + Map atts = attributes.get(featureType); if (atts != null) { AttributeData attData = atts.get(attName); @@ -217,9 +275,9 @@ public class FeatureAttributes * @param attName * @return */ - public float[] getMinMax(String featureType, String attName) + public float[] getMinMax(String featureType, String... attName) { - Map atts = attributes.get(featureType); + Map atts = attributes.get(featureType); if (atts != null) { AttributeData attData = atts.get(attName); @@ -238,19 +296,18 @@ public class FeatureAttributes * @param attName * @param description */ - public void addDescription(String featureType, String attName, - String description) + public void addDescription(String featureType, String description, + String... attName) { if (featureType == null || attName == null) { return; } - Map atts = attributes.get(featureType); + Map atts = attributes.get(featureType); if (atts == null) { - atts = new TreeMap( - String.CASE_INSENSITIVE_ORDER); + atts = new TreeMap<>(comparator); attributes.put(featureType, atts); } AttributeData attData = atts.get(attName); diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index 6c85e8c..da3819c 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -57,6 +57,8 @@ import javax.swing.event.ChangeListener; public class FeatureColourChooser extends JalviewDialog { + private static final String COLON = ":"; + private static final int MAX_TOOLTIP_LENGTH = 50; private static int NO_COLOUR_OPTION = 0; @@ -163,9 +165,8 @@ public class FeatureColourChooser extends JalviewDialog this.fr = frender; this.type = theType; ap = fr.ap; - String title = MessageManager - .formatMessage("label.graduated_color_for_params", new String[] - { theType }); + String title = MessageManager.formatMessage("label.variable_color_for", + new String[] { theType }); initDialogFrame(this, true, blocking, title, 470, 300); slider.addChangeListener(new ChangeListener() @@ -260,7 +261,9 @@ public class FeatureColourChooser extends JalviewDialog { byAttributeText.setSelected(true); textAttributeCombo.setEnabled(true); - textAttributeCombo.setSelectedItem(cs.getAttributeName()); + String[] attributeName = cs.getAttributeName(); + textAttributeCombo + .setSelectedItem(String.join(COLON, attributeName)); } else { @@ -273,8 +276,9 @@ public class FeatureColourChooser extends JalviewDialog if (cs.isColourByAttribute()) { byAttributeValue.setSelected(true); - String attributeName = cs.getAttributeName(); - valueAttributeCombo.setSelectedItem(attributeName); + String[] attributeName = cs.getAttributeName(); + valueAttributeCombo + .setSelectedItem(String.join(COLON, attributeName)); valueAttributeCombo.setEnabled(true); updateMinMax(); } @@ -389,7 +393,8 @@ public class FeatureColourChooser extends JalviewDialog else if (byAttributeValue.isSelected()) { String attName = (String) valueAttributeCombo.getSelectedItem(); - minMax = FeatureAttributes.getInstance().getMinMax(type, attName); + String[] attNames = attName.split(COLON); + minMax = FeatureAttributes.getInstance().getMinMax(type, attNames); } if (minMax != null) { @@ -428,8 +433,8 @@ public class FeatureColourChooser extends JalviewDialog byAttributeValue.addActionListener(changeMinMaxAction); byWhatPanel.add(byAttributeValue); - List attNames = FeatureAttributes.getInstance().getAttributes( - type); + List attNames = FeatureAttributes.getInstance() + .getAttributes(type); valueAttributeCombo = populateAttributesDropdown(type, attNames, true); /* @@ -632,8 +637,8 @@ public class FeatureColourChooser extends JalviewDialog byAttributeText.addActionListener(changeColourAction); byTextPanel.add(byAttributeText); - List attNames = FeatureAttributes.getInstance().getAttributes( - type); + List attNames = FeatureAttributes.getInstance() + .getAttributes(type); textAttributeCombo = populateAttributesDropdown(type, attNames, false); byTextPanel.add(textAttributeCombo); @@ -735,13 +740,18 @@ public class FeatureColourChooser extends JalviewDialog { attribute = (String) textAttributeCombo.getSelectedItem(); textAttributeCombo.setEnabled(true); + acg.setAttributeName(attribute.split(COLON)); } else if (byAttributeValue.isSelected()) { attribute = (String) valueAttributeCombo.getSelectedItem(); valueAttributeCombo.setEnabled(true); + acg.setAttributeName(attribute.split(COLON)); + } + else + { + acg.setAttributeName((String) null); } - acg.setAttributeName(attribute); if (!hasThreshold) { @@ -933,23 +943,26 @@ public class FeatureColourChooser extends JalviewDialog /** * A helper method to build the drop-down choice of attributes for a feature. * Where metadata is available with a description for an attribute, that is - * added as a tooltip. The list may be restricted to attributes for which we - * hold a range of numerical values (so suitable candidates for a graduated - * colour scheme). + * added as a tooltip. The list may optionally be restricted to attributes for + * which we hold a range of numerical values (so suitable candidates for a + * graduated colour scheme). + *

+ * Attribute names may be 'simple' e.g. "AC" or 'compound' e.g. {"CSQ", + * "Allele"}. Compound names are rendered for display as (e.g.) CSQ:Allele. * * @param featureType * @param attNames * @param withNumericRange */ protected JComboBox populateAttributesDropdown( - String featureType, List attNames, + String featureType, List attNames, boolean withNumericRange) { List validAtts = new ArrayList<>(); List tooltips = new ArrayList<>(); FeatureAttributes fa = FeatureAttributes.getInstance(); - for (String attName : attNames) + for (String[] attName : attNames) { if (withNumericRange) { @@ -959,7 +972,7 @@ public class FeatureColourChooser extends JalviewDialog continue; } } - validAtts.add(attName); + validAtts.add(String.join(COLON, attName)); String desc = fa.getDescription(featureType, attName); if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH) { diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 01c40d5..ed98830 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -120,6 +120,8 @@ import javax.swing.table.TableCellRenderer; public class FeatureSettings extends JPanel implements FeatureSettingsControllerI { + private static final String COLON = ":"; + private static final int MIN_WIDTH = 400; private static final int MIN_HEIGHT = 400; @@ -1486,8 +1488,8 @@ public class FeatureSettings extends JPanel * look up attributes known for feature type */ String selectedType = (String) filteredFeatureChoice.getSelectedItem(); - List attNames = FeatureAttributes.getInstance().getAttributes( - selectedType); + List attNames = FeatureAttributes.getInstance() + .getAttributes(selectedType); /* * if this feature type has filters set, load them first @@ -1507,7 +1509,8 @@ public class FeatureSettings extends JPanel /* * and an empty filter for the user to populate (add) */ - KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], ""); + KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "", + (String) null); filters.add(noFilter); /* @@ -1516,11 +1519,11 @@ public class FeatureSettings extends JPanel int filterIndex = 0; for (KeyedMatcherI filter : filters) { - String key = filter.getKey(); + String[] attName = filter.getKey(); Condition condition = filter.getMatcher() .getCondition(); String pattern = filter.getMatcher().getPattern(); - JPanel row = addFilter(key, attNames, condition, pattern, filterIndex); + JPanel row = addFilter(attName, attNames, condition, pattern, filterIndex); row.setBorder(BorderFactory.createLineBorder(debugBorderColour)); chooseFiltersPanel.add(row); filterIndex++; @@ -1539,18 +1542,18 @@ public class FeatureSettings extends JPanel *

  • a text field for input of a match pattern
  • *
  • optionally, a 'remove' button
  • * - * 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). + * 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). * - * @param attribute + * @param attName * @param attNames * @param cond * @param pattern * @param filterIndex * @return */ - protected JPanel addFilter(String attribute, List attNames, + protected JPanel addFilter(String[] attName, List attNames, Condition cond, String pattern, int filterIndex) { JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); @@ -1593,13 +1596,13 @@ public class FeatureSettings extends JPanel } }; - if ("".equals(attribute)) + if (attName == null) // the 'add a condition' row { attCombo.setSelectedItem(null); } else { - attCombo.setSelectedItem(attribute); + attCombo.setSelectedItem(String.join(COLON, attName)); } attCombo.addItemListener(itemListener); @@ -1667,22 +1670,24 @@ public class FeatureSettings extends JPanel * @param attNames */ protected JComboBox populateAttributesDropdown( - String featureType, List attNames) + String featureType, List attNames) { + List displayNames = new ArrayList<>(); List tooltips = new ArrayList<>(); FeatureAttributes fa = FeatureAttributes.getInstance(); - for (String attName : attNames) + for (String[] attName : attNames) { String desc = fa.getDescription(featureType, attName); if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH) { desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "..."; } + displayNames.add(String.join(COLON, attName)); tooltips.add(desc == null ? "" : desc); } JComboBox attCombo = JvSwingUtils.buildComboWithTooltips( - attNames, tooltips); + displayNames, tooltips); if (attNames.isEmpty()) { attCombo.setToolTipText(MessageManager @@ -1760,7 +1765,8 @@ public class FeatureSettings extends JPanel String attName = (String) attCombo.getSelectedItem(); Condition cond = (Condition) condCombo.getSelectedItem(); String pattern = valueField.getText(); - KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern); + KeyedMatcherI km = new KeyedMatcher(cond, pattern, + attName.split(COLON)); filters.set(filterIndex, km); } @@ -2134,7 +2140,7 @@ public class FeatureSettings extends JPanel if (gcol.isColourByAttribute()) { - tx.append(gcol.getAttributeName()); + tx.append(String.join(":", gcol.getAttributeName())); } else if (!gcol.isColourByLabel()) { diff --git a/src/jalview/io/SequenceAnnotationReport.java b/src/jalview/io/SequenceAnnotationReport.java index 1f92428..6b82671 100644 --- a/src/jalview/io/SequenceAnnotationReport.java +++ b/src/jalview/io/SequenceAnnotationReport.java @@ -210,11 +210,12 @@ public class SequenceAnnotationReport FeatureColourI fc = fr.getFeatureColours().get(feature.getType()); if (fc != null && fc.isColourByAttribute()) { - String attName = fc.getAttributeName(); + String[] attName = fc.getAttributeName(); String attVal = feature.getValueAsString(attName); if (attVal != null) { - sb.append("; ").append(attName).append("=").append(attVal); + sb.append("; ").append(String.join(":", attName)).append("=") + .append(attVal); } } } @@ -301,7 +302,7 @@ public class SequenceAnnotationReport */ Collection> createLinksFrom(SequenceI seq, String link) { - Map> urlSets = new LinkedHashMap>(); + Map> urlSets = new LinkedHashMap<>(); UrlLink urlLink = new UrlLink(link); if (!urlLink.isValid()) { diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java index 168ab54..71a89b0 100644 --- a/src/jalview/schemes/FeatureColour.java +++ b/src/jalview/schemes/FeatureColour.java @@ -78,10 +78,10 @@ public class FeatureColour implements FeatureColourI private boolean colourByLabel; /* - * if not null, the value of this named attribute is used for - * colourByLabel or graduatedColour + * if not null, the value of [attribute, [sub-attribute] ...] + * is used for colourByLabel or graduatedColour */ - private String byAttributeName; + private String[] attributeName; private float threshold; @@ -371,7 +371,7 @@ public class FeatureColour implements FeatureColourI base = fc.base; range = fc.range; isHighToLow = fc.isHighToLow; - byAttributeName = fc.byAttributeName; + attributeName = fc.attributeName; setAboveThreshold(fc.isAboveThreshold()); setBelowThreshold(fc.isBelowThreshold()); setThreshold(fc.getThreshold()); @@ -593,8 +593,8 @@ public class FeatureColour implements FeatureColourI { if (isColourByLabel()) { - String label = byAttributeName == null ? feature.getDescription() - : feature.getValueAsString(byAttributeName); + String label = attributeName == null ? feature.getDescription() + : feature.getValueAsString(attributeName); return label == null ? noColour : ColorUtils .createColourFromName(label); } @@ -611,11 +611,11 @@ public class FeatureColour implements FeatureColourI * no such attribute is assigned the 'no value' colour */ float scr = feature.getScore(); - if (byAttributeName != null) + if (attributeName != null) { try { - String attVal = feature.getValueAsString(byAttributeName); + String attVal = feature.getValueAsString(attributeName); scr = Float.valueOf(attVal); } catch (Throwable e) { @@ -746,19 +746,19 @@ public class FeatureColour implements FeatureColourI @Override public boolean isColourByAttribute() { - return byAttributeName != null; + return attributeName != null; } @Override - public String getAttributeName() + public String[] getAttributeName() { - return byAttributeName; + return attributeName; } @Override - public void setAttributeName(String name) + public void setAttributeName(String... name) { - byAttributeName = name; + attributeName = name; } } diff --git a/test/jalview/datamodel/features/FeatureAttributesTest.java b/test/jalview/datamodel/features/FeatureAttributesTest.java new file mode 100644 index 0000000..e464326 --- /dev/null +++ b/test/jalview/datamodel/features/FeatureAttributesTest.java @@ -0,0 +1,41 @@ +package jalview.datamodel.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Comparator; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +public class FeatureAttributesTest +{ + + /** + * Test the method that keeps attribute names in non-case-sensitive order, + * including handling of 'compound' names + */ + @Test(groups="Functional") + public void testAttributeNameComparator() + { + FeatureAttributes fa = FeatureAttributes.getInstance(); + Comparator comp = (Comparator) PA.getValue(fa, + "comparator"); + + assertEquals( + comp.compare(new String[] { "CSQ" }, new String[] { "csq" }), 0); + + assertTrue(comp.compare(new String[] { "CSQ", "a" }, + new String[] { "csq" }) > 0); + + assertTrue(comp.compare(new String[] { "CSQ" }, new String[] { "csq", + "b" }) < 0); + + assertTrue(comp.compare(new String[] { "CSQ", "AF" }, new String[] { + "csq", "ac" }) > 0); + + assertTrue(comp.compare(new String[] { "CSQ", "ac" }, new String[] { + "csq", "AF" }) < 0); + } +} -- 1.7.10.2