Permalink
Browse files

fixed the text codec to enable empty strings TA:Z:"" as per latest h…

…ts-spec

* Added a test to show that htsJDK can handle empty strings as tag values.

* - fixed the text codec to enable empty strings
- testing that we can code and decode into various formats

- Enables the reading and writing of sam/bam/cram records with empty string tags as required by samtools/hts-specs#135
1 parent fbba536 commit b5fd3c02b3140b8bab4ec7436c1e21e4604a63f4 @yfarjoun yfarjoun committed on GitHub Sep 19, 2016
@@ -1397,7 +1397,7 @@ protected void setAttribute(final short tag, final Object value) {
* @deprecated
* The attribute type and value checks have been moved directly into
- * {@code SAMBinaryTagAndValue}.
+ * {@link SAMBinaryTagAndValue}.
*/
@Deprecated
protected static boolean isAllowedAttributeValue(final Object value) {
@@ -41,6 +41,7 @@
* instance is used in multiple threads.
*/
public class TextTagCodec {
+ // 3 fields for non-empty strings 2 fields if the string is empty.
private static final int NUM_TAG_FIELDS = 3;
/**
@@ -149,12 +150,12 @@ public String encodeUntypedTag(final String tagName, final Object value) {
*/
public Map.Entry<String, Object> decode(final String tag) {
final int numFields = StringUtil.splitConcatenateExcessTokens(tag, fields, ':');
- if (numFields != TextTagCodec.NUM_TAG_FIELDS) {
+ if (numFields != TextTagCodec.NUM_TAG_FIELDS && numFields != TextTagCodec.NUM_TAG_FIELDS - 1) {
throw new SAMFormatException("Not enough fields in tag '" + tag + "'");
}
final String key = fields[0];
final String type = fields[1];
- final String stringVal = fields[2];
+ final String stringVal = numFields == TextTagCodec.NUM_TAG_FIELDS ? fields[2] : "";
final Object val = convertStringToObject(type, stringVal);
return new Map.Entry<String, Object>() {
public String getKey() {
@@ -98,6 +98,52 @@ public void testGetTypedAttributeMethods() throws Exception {
Assert.assertEquals(rec.getIntegerAttribute(INTEGER_TAG).intValue(), 1);
}
+
+ @DataProvider
+ public Object[][] formatsAndValues(){
+ return new Object[][]{
+ new Object[]{"sam","Hello World!"},
+ new Object[]{"bam","Hello World!"},
+ new Object[]{"cram","Hello World!"},
+ new Object[]{"cram",""},
+ new Object[]{"bam",""},
+ new Object[]{"sam",""},
+ };
+ }
+ /**
+ * Should be able to write empty and non-empty strings
+ */
+ @Test(dataProvider = "formatsAndValues")
+ public void testWriteAndReadStrings(final String format,final String value) throws Exception {
+ final SAMRecord rec = createSamRecord();
+ rec.setAttribute(STRING_TAG, value);
+ writeAndReadSamRecord(format, rec);
+ Assert.assertEquals(rec.getStringAttribute(STRING_TAG),value);
+ }
+
+
+ @DataProvider
+ public Object[][] formatsAndValues2(){
+ return new Object[][]{
+ new Object[]{"sam",'a'},
+ new Object[]{"bam",'a'},
+ new Object[]{"cram",'a'},
+ new Object[]{"cram",null},
+ new Object[]{"bam",null},
+ new Object[]{"sam",null},
+ };
+ }
+ /**
+ * Should be able to write empty and non-empty strings
+ */
+ @Test(dataProvider = "formatsAndValues2")
+ public void testWriteAndReadCharacters(final String format,final Character value) throws Exception {
+ final SAMRecord rec = createSamRecord();
+ rec.setAttribute(STRING_TAG, value);
+ writeAndReadSamRecord(format, rec);
+ Assert.assertEquals(rec.getCharacterAttribute(STRING_TAG),value);
+ }
+
/**
* Should be an exception if a typed attribute call is made for the wrong type.
*/
@@ -417,6 +417,19 @@ public void test_isAllowedAttributeDataType() {
Assert.assertFalse(SAMRecord.isAllowedAttributeValue(new Long(Integer.MIN_VALUE - 1L)));
}
+ @Test()
+ public void test_setAttribute_empty_string() {
+ final SAMFileHeader header = new SAMFileHeader();
+ final SAMRecord record = new SAMRecord(header);
+ Assert.assertNull(record.getStringAttribute(SAMTag.MD.name()));
+ record.setAttribute(SAMTag.MD.name(), "");
+ Assert.assertNotNull(record.getStringAttribute(SAMTag.MD.name()));
+ Assert.assertEquals(record.getStringAttribute(SAMTag.MD.name()),"");
+ record.setAttribute(SAMTag.MD.name(), null);
+ Assert.assertNull(record.getStringAttribute(SAMTag.MD.name()));
+ }
+
+
@Test(expectedExceptions = IllegalArgumentException.class)
public void test_setAttribute_unsigned_int_negative() {
SAMFileHeader header = new SAMFileHeader();

0 comments on commit b5fd3c0

Please sign in to comment.