Skip to content

Commit

Permalink
Expose the set of tags to reverse and reverse complement as parameter…
Browse files Browse the repository at this point in the history
…s. (#673)
  • Loading branch information
tfenne authored Aug 2, 2016
1 parent 0875b62 commit ec2d3f9
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 28 deletions.
104 changes: 76 additions & 28 deletions src/main/java/htsjdk/samtools/SAMRecordUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,56 +26,104 @@
import htsjdk.samtools.util.SequenceUtil;
import htsjdk.samtools.util.StringUtil;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
* @author [email protected]
*/
public class SAMRecordUtil {

/** List of String tags that must be reversed if present when a SAMRecord is reverseComplemented */
private static final short[] STRING_TAGS_TO_REVERSE = {
SAMTagUtil.getSingleton().U2,
SAMTagUtil.getSingleton().OQ
};
public static List<String> TAGS_TO_REVERSE_COMPLEMENT = Arrays.asList(SAMTag.E2.name(), SAMTag.SQ.name());
public static List<String> TAGS_TO_REVERSE = Arrays.asList(SAMTag.OQ.name(), SAMTag.U2.name());

/**
* Reverse-complement all known sequence and base quality attributes of the SAMRecord.
* Reverse-complement bases and reverse quality scores along with known optional attributes that
* need the same treatment. See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE}
* for the default set of tags that are handled.
*/
public static void reverseComplement(final SAMRecord rec) {
reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE);
}

/**
* Reverse complement bases and reverse quality scores. In addition reverse complement any
* non-null attributes specified by tagsToRevcomp and reverse and non-null attributes
* specified by tagsToReverse.
*/
public static void reverseComplement(final SAMRecord rec, final Collection<String> tagsToRevcomp, final Collection<String> tagsToReverse) {
final byte[] readBases = rec.getReadBases();
SequenceUtil.reverseComplement(readBases);
rec.setReadBases(readBases);
final byte qualities[] = rec.getBaseQualities();
reverseArray(qualities);
rec.setBaseQualities(qualities);
final byte[] sqTagValue = (byte[])rec.getAttribute(SAMTagUtil.getSingleton().SQ);
if (sqTagValue != null) {
SQTagUtil.reverseComplementSqArray(sqTagValue);
rec.setAttribute(SAMTagUtil.getSingleton().SQ, sqTagValue);
}
final String e2TagValue = (String)rec.getAttribute(SAMTagUtil.getSingleton().E2);
if (e2TagValue != null) {
final byte[] secondaryBases = StringUtil.stringToBytes(e2TagValue);
SequenceUtil.reverseComplement(secondaryBases);
rec.setAttribute(SAMTagUtil.getSingleton().E2, StringUtil.bytesToString(secondaryBases));

// Deal with tags that need to be reverse complemented
if (tagsToRevcomp != null) {
for (final String tag: tagsToRevcomp) {
Object value = rec.getAttribute(tag);
if (value != null) {
if (value instanceof byte[]) SequenceUtil.reverseComplement((byte[]) value);
else if (value instanceof String) value = SequenceUtil.reverseComplement((String) value);
else throw new UnsupportedOperationException("Don't know how to reverse complement: " + value);
rec.setAttribute(tag, value);
}
}
}
for (final short stringTag : STRING_TAGS_TO_REVERSE) {
final String value = (String)rec.getAttribute(stringTag);
if (value != null) {
rec.setAttribute(stringTag, StringUtil.reverseString(value));

// Deal with tags that needed to just be reversed
if (tagsToReverse != null) {
for (final String tag : tagsToReverse) {
Object value = rec.getAttribute(tag);
if (value != null) {
if (value instanceof String) {
value = StringUtil.reverseString((String) value);
}
else if (value.getClass().isArray()) {
if (value instanceof byte[]) reverseArray((byte[]) value);
else if (value instanceof short[]) reverseArray((short[]) value);
else if (value instanceof int[]) reverseArray((int[]) value);
else if (value instanceof float[]) reverseArray((float[]) value);
else throw new UnsupportedOperationException("Reversing array attribute of type " + value.getClass().getComponentType() + " not supported.");
}
else throw new UnsupportedOperationException("Don't know how to reverse: " + value);

rec.setAttribute(tag, value);
}
}
}
}

/**
* Reverse the given array in place.
*/
public static void reverseArray(final byte[] array) {
final int lastIndex = array.length - 1;
int i, j;
for (i=0, j=lastIndex; i<j; ++i, --j) {
private static void reverseArray(final byte[] array) {
for (int i=0, j=array.length-1; i<j; ++i, --j) {
final byte tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}

private static void reverseArray(final short[] array) {
for (int i=0, j=array.length-1; i<j; ++i, --j) {
final short tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}

private static void reverseArray(final int[] array) {
for (int i=0, j=array.length-1; i<j; ++i, --j) {
final int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}

private static void reverseArray(final float[] array) {
for (int i=0, j=array.length-1; i<j; ++i, --j) {
final float tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
29 changes: 29 additions & 0 deletions src/test/java/htsjdk/samtools/SAMRecordUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package htsjdk.samtools;

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.Arrays;

public class SAMRecordUtilTest {
@Test public void testReverseComplement() {
final SAMFileHeader header = new SAMFileHeader();
final SAMRecord rec = new SAMRecord(header);
rec.setReadString("ACACACACAC");
rec.setBaseQualityString("HHHHHIIIII");
rec.setAttribute("X1", new byte[] {1,2,3,4,5});
rec.setAttribute("X2", new short[] {1,2,3,4,5});
rec.setAttribute("X3", new int[] {1,2,3,4,5});
rec.setAttribute("X4", new float[] {1.0f,2.0f,3.0f,4.0f,5.0f});
rec.setAttribute("Y1", "AAAAGAAAAC");

SAMRecordUtil.reverseComplement(rec, Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"));
Assert.assertEquals(rec.getReadString(), "GTGTGTGTGT");
Assert.assertEquals(rec.getBaseQualityString(), "IIIIIHHHHH");
Assert.assertEquals(rec.getByteArrayAttribute("X1"), new byte[] {5,4,3,2,1});
Assert.assertEquals(rec.getSignedShortArrayAttribute("X2"), new short[] {5,4,3,2,1});
Assert.assertEquals(rec.getSignedIntArrayAttribute("X3"), new int[] {5,4,3,2,1});
Assert.assertEquals(rec.getFloatArrayAttribute("X4"), new float[] {5.0f,4.0f,3.0f,2.0f,1.0f});
Assert.assertEquals(rec.getStringAttribute("Y1"), "GTTTTCTTTT");
}
}

0 comments on commit ec2d3f9

Please sign in to comment.