diff --git a/src/main/java/htsjdk/variant/variantcontext/Allele.java b/src/main/java/htsjdk/variant/variantcontext/Allele.java index dfd11d892f..0151a21af2 100644 --- a/src/main/java/htsjdk/variant/variantcontext/Allele.java +++ b/src/main/java/htsjdk/variant/variantcontext/Allele.java @@ -126,13 +126,14 @@ public class Allele implements Comparable, Serializable { private byte[] bases = null; /** A generic static NO_CALL allele for use */ - public final static String NO_CALL_STRING = "."; + public static final String NO_CALL_STRING = "."; /** A generic static SPAN_DEL allele for use */ - public final static String SPAN_DEL_STRING = "*"; + public static final String SPAN_DEL_STRING = "*"; - /** A generic static NON_REF allele for use */ - public final static String NON_REF_STRING = ""; + /** Non ref allele representations */ + public static final String NON_REF_STRING = ""; + public static final String UNSPECIFIED_ALTERNATE_ALLELE_STRING = "<*>"; // no public way to create an allele protected Allele(final byte[] bases, final boolean isRef) { @@ -184,31 +185,31 @@ protected Allele(final Allele allele, final boolean ignoreRefState) { this.isSymbolic = allele.isSymbolic; } - - private final static Allele REF_A = new Allele("A", true); - private final static Allele ALT_A = new Allele("A", false); - private final static Allele REF_C = new Allele("C", true); - private final static Allele ALT_C = new Allele("C", false); - private final static Allele REF_G = new Allele("G", true); - private final static Allele ALT_G = new Allele("G", false); - private final static Allele REF_T = new Allele("T", true); - private final static Allele ALT_T = new Allele("T", false); - private final static Allele REF_N = new Allele("N", true); - private final static Allele ALT_N = new Allele("N", false); - public final static Allele SPAN_DEL = new Allele(SPAN_DEL_STRING, false); - public final static Allele NO_CALL = new Allele(NO_CALL_STRING, false); - public final static Allele NON_REF_ALLELE = new Allele(NON_REF_STRING, false); + private static final Allele REF_A = new Allele("A", true); + private static final Allele ALT_A = new Allele("A", false); + private static final Allele REF_C = new Allele("C", true); + private static final Allele ALT_C = new Allele("C", false); + private static final Allele REF_G = new Allele("G", true); + private static final Allele ALT_G = new Allele("G", false); + private static final Allele REF_T = new Allele("T", true); + private static final Allele ALT_T = new Allele("T", false); + private static final Allele REF_N = new Allele("N", true); + private static final Allele ALT_N = new Allele("N", false); + public static final Allele SPAN_DEL = new Allele(SPAN_DEL_STRING, false); + public static final Allele NO_CALL = new Allele(NO_CALL_STRING, false); + public static final Allele NON_REF_ALLELE = new Allele(NON_REF_STRING, false); + public static final Allele UNSPECIFIED_ALTERNATE_ALLELE = new Allele(UNSPECIFIED_ALTERNATE_ALLELE_STRING, false); // for simple deletion, e.g. "ALT==" (note that the spec allows, for now at least, alt alleles like ) - public final static Allele SV_SIMPLE_DEL = StructuralVariantType.DEL.toSymbolicAltAllele(); + public static final Allele SV_SIMPLE_DEL = StructuralVariantType.DEL.toSymbolicAltAllele(); // for simple insertion, e.g. "ALT==" - public final static Allele SV_SIMPLE_INS = StructuralVariantType.INS.toSymbolicAltAllele(); + public static final Allele SV_SIMPLE_INS = StructuralVariantType.INS.toSymbolicAltAllele(); // for simple inversion, e.g. "ALT==" - public final static Allele SV_SIMPLE_INV = StructuralVariantType.INV.toSymbolicAltAllele(); + public static final Allele SV_SIMPLE_INV = StructuralVariantType.INV.toSymbolicAltAllele(); // for simple generic cnv, e.g. "ALT==" - public final static Allele SV_SIMPLE_CNV = StructuralVariantType.CNV.toSymbolicAltAllele(); + public static final Allele SV_SIMPLE_CNV = StructuralVariantType.CNV.toSymbolicAltAllele(); // for simple duplication, e.g. "ALT==" - public final static Allele SV_SIMPLE_DUP = StructuralVariantType.DUP.toSymbolicAltAllele(); + public static final Allele SV_SIMPLE_DUP = StructuralVariantType.DUP.toSymbolicAltAllele(); // --------------------------------------------------------------------------------------------------------- // @@ -558,4 +559,11 @@ private static boolean firstIsPrefixOfSecond(final Allele a1, final Allele a2) { String a1String = a1.getBaseString(); return a2.getBaseString().substring(0, a1String.length()).equals(a1String); } + + /** + * @return true if Allele is either {@code } or {@code <*>} + */ + public boolean isNonRefAllele() { + return equals(NON_REF_ALLELE) || equals(UNSPECIFIED_ALTERNATE_ALLELE); + } } diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 46b124496d..9fc6c7b839 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -25,7 +25,6 @@ package htsjdk.variant.variantcontext; -import htsjdk.samtools.util.Locatable; import htsjdk.tribble.Feature; import htsjdk.tribble.TribbleException; import htsjdk.tribble.util.ParsingUtils; @@ -1248,11 +1247,11 @@ public void validateChromosomeCounts() { validateAttributeIsExpectedSize(VCFConstants.ALLELE_COUNT_KEY, numberOfAlternateAlleles); validateAttributeIsExpectedSize(VCFConstants.ALLELE_FREQUENCY_KEY, numberOfAlternateAlleles); - if ( !hasGenotypes() ) + if (!hasGenotypes()) return; // AN - if ( hasAttribute(VCFConstants.ALLELE_NUMBER_KEY) ) { + if (hasAttribute(VCFConstants.ALLELE_NUMBER_KEY)) { final int reportedAN = Integer.valueOf(getAttribute(VCFConstants.ALLELE_NUMBER_KEY).toString()); final int observedAN = getCalledChrCount(); if ( reportedAN != observedAN ) @@ -1260,12 +1259,12 @@ public void validateChromosomeCounts() { } // AC - if ( hasAttribute(VCFConstants.ALLELE_COUNT_KEY) ) { + if (hasAttribute(VCFConstants.ALLELE_COUNT_KEY)) { final ArrayList observedACs = new ArrayList<>(); // if there are alternate alleles, record the relevant tags - if ( numberOfAlternateAlleles > 0 ) { - for ( Allele allele : getAlternateAlleles() ) { + if (numberOfAlternateAlleles > 0) { + for (Allele allele : getAlternateAlleles()) { observedACs.add(getCalledChrCount(allele)); } } @@ -1278,7 +1277,7 @@ public void validateChromosomeCounts() { for (int i = 0; i < observedACs.size(); i++) { // need to cast to int to make sure we don't have an issue below with object equals (earlier bug) - EB final int reportedAC = Integer.valueOf(reportedACs.get(i).toString()); - if ( reportedAC != observedACs.get(i) ) + if (reportedAC != observedACs.get(i)) throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag is incorrect for the record at position %s:%d, %s vs. %d", getContig(), getStart(), reportedAC, observedACs.get(i))); } } @@ -1682,11 +1681,22 @@ public int getEnd() { return (int)stop; } + /** + * + * @return true if the variant context is a reference block + * + */ + public boolean isReferenceBlock() { + return getAlternateAlleles().size() == 1 + && getAlternateAllele(0).isNonRefAllele() + && getAttribute(VCFConstants.END_KEY) != null; + } + public boolean hasSymbolicAlleles() { return hasSymbolicAlleles(getAlleles()); } - public static boolean hasSymbolicAlleles( final List alleles ) { + public static boolean hasSymbolicAlleles(final List alleles) { return alleles.stream().anyMatch(Allele::isSymbolic); } diff --git a/src/test/java/htsjdk/variant/variantcontext/AlleleUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/AlleleUnitTest.java index c2aa79ff38..e96c2680d8 100644 --- a/src/test/java/htsjdk/variant/variantcontext/AlleleUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/AlleleUnitTest.java @@ -29,7 +29,6 @@ // the imports for unit testing. import htsjdk.variant.VariantBaseTest; -import htsjdk.variant.variantcontext.Allele; import org.testng.Assert; import org.testng.annotations.BeforeSuite; @@ -48,7 +47,7 @@ * Basic unit test for RecalData */ public class AlleleUnitTest extends VariantBaseTest { - Allele ARef, A, T, ATIns, ATCIns, NoCall, SpandDel; + private Allele ARef, A, T, ATIns, ATCIns, NoCall, SpandDel, NonRef, UnspecifiedAlternate; @BeforeSuite public void before() { @@ -62,6 +61,9 @@ public void before() { NoCall = Allele.create(Allele.NO_CALL_STRING); SpandDel = Allele.create(Allele.SPAN_DEL_STRING); + + NonRef = Allele.create(Allele.NON_REF_STRING); + UnspecifiedAlternate = Allele.create(Allele.UNSPECIFIED_ALTERNATE_ALLELE_STRING); } @Test @@ -189,6 +191,22 @@ public void testSymbolic() { Assert.assertEquals("", a.getDisplayString()); } + @Test + public void testNonRefAllele() { + Assert.assertTrue(NonRef.isNonRefAllele()); + Assert.assertTrue(UnspecifiedAlternate.isNonRefAllele()); + + Assert.assertFalse(T.isNonRefAllele()); + Assert.assertFalse(ATIns.isNonRefAllele()); + + Assert.assertTrue(Allele.NON_REF_ALLELE.isNonRefAllele()); + Assert.assertTrue(Allele.UNSPECIFIED_ALTERNATE_ALLELE.isNonRefAllele()); + + Allele a = Allele.create(new String("<*>")); + Assert.assertTrue(a.isNonRefAllele()); + } + + @Test public void testEquals() { Assert.assertTrue(ARef.basesMatch(A)); @@ -259,4 +277,4 @@ public void testExtend() { Assert.assertEquals("ATCGA", Allele.extend(Allele.create("AT"), "CGA".getBytes()).toString()); Assert.assertEquals("ATCGA", Allele.extend(Allele.create("ATC"), "GA".getBytes()).toString()); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 3d68515983..5081315634 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -1552,6 +1552,28 @@ public void testExtractStructuralVariationsData(final File vcfFile) { } } + @DataProvider(name = "referenceBlockData") + public Object[][] referenceBlockData() { + return new Object[][]{ + {Arrays.asList(Aref, Allele.UNSPECIFIED_ALTERNATE_ALLELE), false, false}, + {Arrays.asList(Aref, Allele.UNSPECIFIED_ALTERNATE_ALLELE), true, true}, + {Arrays.asList(Aref, Allele.NON_REF_ALLELE), true, true}, + {Arrays.asList(Aref, C, Allele.UNSPECIFIED_ALTERNATE_ALLELE), true, false}, + {Arrays.asList(Aref, C), false, false} + }; + } + + @Test(dataProvider = "referenceBlockData") + public void testReferenceBlock(List alleles, boolean addEndAttribute, boolean isRefBlock) { + // create a context builder/context based on inputs provided + final VariantContextBuilder builder = + new VariantContextBuilder("test", snpLoc,snpLocStart, snpLocStop, alleles); + if (addEndAttribute) { + builder.attribute("END", 10); + } + Assert.assertEquals(builder.make().isReferenceBlock(), isRefBlock); + } + @Test public void testGetAttributeAsIntList() { final VariantContext context = basicBuilder