diff --git a/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java b/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java index 88b02fafb..e2f9083cf 100644 --- a/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java +++ b/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java @@ -37,6 +37,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; /** @@ -243,18 +245,62 @@ public Object getAttribute(String key, Object defaultValue) { return defaultValue; } - /** returns the value as an empty list if the key was not found, - as a java.util.List if the value is a List or an Array, - as a Collections.singletonList if there is only one value */ + /** + * Gets the attributes from a key as a list. + * + * Note: int[] and double[] arrays are boxed. + * + * @return empty list if the key was not found; {@link Collections#singletonList(Object)} if + * there is only one value; a list containing the values if the value is a {@link List} or array. + */ @SuppressWarnings("unchecked") public List getAttributeAsList(String key) { Object o = getAttribute(key); if ( o == null ) return Collections.emptyList(); if ( o instanceof List ) return (List)o; - if ( o.getClass().isArray() ) return Arrays.asList((Object[])o); + if ( o.getClass().isArray() ) { + if (o instanceof int[]) { + return Arrays.stream((int[])o).boxed().collect(Collectors.toList()); + } else if (o instanceof double[]) { + return Arrays.stream((double[])o).boxed().collect(Collectors.toList()); + } + return Arrays.asList((Object[])o); + } return Collections.singletonList(o); } + private List getAttributeAsList(String key, Function transformer) { + return getAttributeAsList(key).stream().map(transformer).collect(Collectors.toList()); + } + + public List getAttributeAsStringList(String key, String defaultValue) { + return getAttributeAsList(key, x -> (x == null) ? defaultValue : String.valueOf(x)); + } + + public List getAttributeAsIntList(String key, Integer defaultValue) { + return getAttributeAsList(key, x -> { + if (x == null || x == VCFConstants.MISSING_VALUE_v4) { + return defaultValue; + } else if (x instanceof Number) { + return ((Number) x).intValue(); + } else { + return Integer.valueOf((String)x); // throws an exception if this isn't a string + } + }); + } + + public List getAttributeAsDoubleList(String key, Double defaultValue) { + return getAttributeAsList(key, x -> { + if (x == null || x == VCFConstants.MISSING_VALUE_v4) { + return defaultValue; + } else if (x instanceof Number) { + return ((Number) x).doubleValue(); + } else { + return Double.valueOf((String)x); // throws an exception if this isn't a string + } + }); + } + public String getAttributeAsString(String key, String defaultValue) { Object x = getAttribute(key); if ( x == null ) return defaultValue; diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 0baf0bd70..5c94bff24 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -747,7 +747,9 @@ public Object getAttribute(String key, Object defaultValue) { as a java.util.List if the value is a List or an Array, as a Collections.singletonList if there is only one value */ public List getAttributeAsList(String key) { return commonInfo.getAttributeAsList(key); } - + public List getAttributeAsStringList(String key, String defaultValue) { return commonInfo.getAttributeAsStringList(key, defaultValue); } + public List getAttributeAsIntList(String key, int defaultValue) { return commonInfo.getAttributeAsIntList(key, defaultValue); } + public List getAttributeAsDoubleList(String key, double defaultValue) { return commonInfo.getAttributeAsDoubleList(key, defaultValue); } public CommonInfo getCommonInfo() { return commonInfo; } diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 34854eaea..32e5596b5 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -38,7 +38,6 @@ import htsjdk.variant.bcf2.BCF2Codec; import htsjdk.variant.vcf.VCFCodec; import htsjdk.tribble.TribbleException; -import htsjdk.variant.VariantBaseTest; import htsjdk.variant.vcf.VCFConstants; import htsjdk.variant.vcf.VCFFileReader; @@ -1478,4 +1477,76 @@ public void testExtractStructuralVariationsData(final File vcfFile) { CloserUtil.close(reader); } } + + @Test + public void testGetAttributeAsIntList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsIntList("Empty", 5).isEmpty()); + // test as integer + Assert.assertEquals(context.getAttributeAsIntList("DefaultIntegerList", 5), Arrays.asList(0, 0, 0, 0, 0)); + Assert.assertEquals(context.getAttributeAsIntList("ListWithMissing", 5), Arrays.asList(1, 5, 5)); + Assert.assertEquals(context.getAttributeAsIntList("IntegerList", 5), Arrays.asList(0, 1, 2, 3)); + Assert.assertEquals(context.getAttributeAsIntList("DoubleList", 5), Arrays.asList(1, 1, 2)); + Assert.assertEquals(context.getAttributeAsIntList("StringList", 5), Arrays.asList(1, 2)); + Assert.assertThrows(() -> context.getAttributeAsIntList("NotNumeric", 5)); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsIntList("MissingList", 5).isEmpty()); + } + + @Test + public void testGetAttributeAsDoubleList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsDoubleList("Empty", 5).isEmpty()); + // test as double + Assert.assertEquals(context.getAttributeAsDoubleList("DefaultIntegerList", 5), Arrays.asList(0d, 0d, 0d, 0d, 0d)); + Assert.assertEquals(context.getAttributeAsDoubleList("ListWithMissing", 5), Arrays.asList(1d, 5d, 5d)); + Assert.assertEquals(context.getAttributeAsDoubleList("IntegerList", 5), Arrays.asList(0d, 1d, 2d, 3d)); + Assert.assertEquals(context.getAttributeAsDoubleList("DoubleList", 5), Arrays.asList(1.8, 1.6, 2.1)); + Assert.assertEquals(context.getAttributeAsDoubleList("StringList", 5), Arrays.asList(1d, 2d)); + Assert.assertThrows(() -> context.getAttributeAsDoubleList("NotNumeric", 5)); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsDoubleList("MissingList", 5).isEmpty()); + } + + @Test + public void testGetAttributeAsStringList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsStringList("Empty", "empty").isEmpty()); + // test as string + Assert.assertEquals(context.getAttributeAsStringList("DefaultIntegerList", "empty"), Arrays.asList("0", "0", "0", "0", "0")); + Assert.assertEquals(context.getAttributeAsStringList("ListWithMissing", "empty"), Arrays.asList("1", "empty", "empty")); + Assert.assertEquals(context.getAttributeAsStringList("IntegerList", "empty"), Arrays.asList("0", "1", "2", "3")); + Assert.assertEquals(context.getAttributeAsStringList("DoubleList", "empty"), Arrays.asList("1.8", "1.6", "2.1")); + Assert.assertEquals(context.getAttributeAsStringList("StringList", "empty"), Arrays.asList("1", "2")); + Assert.assertEquals(context.getAttributeAsStringList("NotNumeric", "empty"), Arrays.asList("A", "B")); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsStringList("MissingList", "empty").isEmpty()); + } }