From 680885310ca5bca2a9b1bfd1c2dd79147d6f4b8d Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 22 Nov 2017 09:34:58 +0000 Subject: [PATCH] JAL-2808 capture feature attributes datatype (Character/Number/Mixed) for more intelligent filter conditions --- .../datamodel/features/FeatureAttributes.java | 45 ++++++++++- .../datamodel/features/FeatureAttributesTest.java | 83 +++++++++++++++++++- 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/jalview/datamodel/features/FeatureAttributes.java b/src/jalview/datamodel/features/FeatureAttributes.java index 7221d62..e359b62 100644 --- a/src/jalview/datamodel/features/FeatureAttributes.java +++ b/src/jalview/datamodel/features/FeatureAttributes.java @@ -14,6 +14,11 @@ import java.util.TreeMap; */ public class FeatureAttributes { + public enum Datatype + { + Character, Number, Mixed + } + private static FeatureAttributes instance = new FeatureAttributes(); /* @@ -79,6 +84,8 @@ public class FeatureAttributes */ boolean hasValue = false; + Datatype type; + /** * Note one instance of this attribute, recording unique, non-null names, * and the min/max of any numerical values @@ -95,12 +102,17 @@ public class FeatureAttributes try { float f = Float.valueOf(value); - min = Float.min(min, f); - max = Float.max(max, f); + min = hasValue ? Float.min(min, f) : f; + max = hasValue ? Float.max(max, f) : f; hasValue = true; + type = (type == null || type == Datatype.Number) ? Datatype.Number + : Datatype.Mixed; } catch (NumberFormatException e) { - // ok, wasn't a number, ignore for min-max purposes + // not a number, ignore for min-max purposes + type = (type == null || type == Datatype.Character) + ? Datatype.Character + : Datatype.Mixed; } } } @@ -118,6 +130,11 @@ public class FeatureAttributes return null; } + public Datatype getType() + { + return type; + } + /** * Adds the given description to the list of known descriptions (without * duplication) @@ -318,4 +335,26 @@ public class FeatureAttributes } attData.addDescription(description); } + + /** + * Answers the datatype of the feature, which is one of Character, Number or + * Mixed (or null if not known), as discovered from values recorded. + * + * @param featureType + * @param attName + * @return + */ + public Datatype getDatatype(String featureType, String... attName) + { + Map atts = attributes.get(featureType); + if (atts != null) + { + AttributeData attData = atts.get(attName); + if (attData != null) + { + return attData.getType(); + } + } + return null; + } } diff --git a/test/jalview/datamodel/features/FeatureAttributesTest.java b/test/jalview/datamodel/features/FeatureAttributesTest.java index e464326..4b7a435 100644 --- a/test/jalview/datamodel/features/FeatureAttributesTest.java +++ b/test/jalview/datamodel/features/FeatureAttributesTest.java @@ -1,18 +1,35 @@ package jalview.datamodel.features; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import java.util.Comparator; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.features.FeatureAttributes.Datatype; -import junit.extensions.PA; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; +import junit.extensions.PA; + public class FeatureAttributesTest { /** + * clear down attributes map after tests + */ + @AfterMethod + public void tearDown() + { + FeatureAttributes fa = FeatureAttributes.getInstance(); + ((Map) PA.getValue(fa, "attributes")).clear(); + } + + /** * Test the method that keeps attribute names in non-case-sensitive order, * including handling of 'compound' names */ @@ -38,4 +55,66 @@ public class FeatureAttributesTest assertTrue(comp.compare(new String[] { "CSQ", "ac" }, new String[] { "csq", "AF" }) < 0); } + + @Test + public void testGetMinMax() + { + SequenceFeature sf = new SequenceFeature("Pfam", "desc", 10, 20, + "group"); + FeatureAttributes fa = FeatureAttributes.getInstance(); + assertNull(fa.getMinMax("Pfam", "kd")); + sf.setValue("domain", "xyz"); + assertNull(fa.getMinMax("Pfam", "kd")); + sf.setValue("kd", "some text"); + assertNull(fa.getMinMax("Pfam", "kd")); + sf.setValue("kd", "1.3"); + assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { 1.3f, 1.3f }); + sf.setValue("kd", "-2.6"); + assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { -2.6f, 1.3f }); + Map csq = new HashMap<>(); + csq.put("AF", "-3"); + sf.setValue("CSQ", csq); + assertEquals(fa.getMinMax("Pfam", "CSQ", "AF"), + new float[] + { -3f, -3f }); + csq.put("AF", "4"); + sf.setValue("CSQ", csq); + assertEquals(fa.getMinMax("Pfam", "CSQ", "AF"), + new float[] + { -3f, 4f }); + } + + /** + * Test the method that returns an attribute description, provided it is + * recorded and unique + */ + @Test + public void testGetDescription() + { + FeatureAttributes fa = FeatureAttributes.getInstance(); + // with no description returns null + assertNull(fa.getDescription("Pfam", "kd")); + // with a unique description, returns that value + fa.addDescription("Pfam", "desc1", "kd"); + assertEquals(fa.getDescription("Pfam", "kd"), "desc1"); + // with ambiguous description, returns null + fa.addDescription("Pfam", "desc2", "kd"); + assertNull(fa.getDescription("Pfam", "kd")); + } + + @Test + public void testDatatype() + { + FeatureAttributes fa = FeatureAttributes.getInstance(); + assertNull(fa.getDatatype("Pfam", "kd")); + SequenceFeature sf = new SequenceFeature("Pfam", "desc", 10, 20, + "group"); + sf.setValue("kd", "-1"); + sf.setValue("domain", "Metal"); + sf.setValue("phase", "1"); + sf.setValue("phase", "reverse"); + assertEquals(fa.getDatatype("Pfam", "kd"), Datatype.Number); + assertEquals(fa.getDatatype("Pfam", "domain"), Datatype.Character); + assertEquals(fa.getDatatype("Pfam", "phase"), Datatype.Mixed); + } } -- 1.7.10.2