Skip to content

Commit

Permalink
Updating SAMTag and SAMTagUtils (#1208)
Browse files Browse the repository at this point in the history
* Update SAMTag to include new tags that were introduced in the spec
  * Cellular barcode tags s CB, CR, and CY added
  * Unique molecular identifier tags BZ, MI, OX, QX, and RX added.
* Deprecating tag constants that are for backward compatibility only, there may be code that still needs to interact with these tags for legacy reasons, so we have no plan to actively remove these in the future, but this should disincentivize new use of the deprecated tags.

* Modernizing SAMTag and SAMTagUtil
  * Making methods in SAMTagUtil final and deprecating the use of the getSingleton() method.
  * This is a technically a breaking change to SAMTagUtil, it will break any subclasses, all other uses should be compatible and we do not believe there are any subclasses in the wild
  • Loading branch information
lbergelson authored Nov 14, 2018
1 parent 9a4b241 commit 5b897e4
Show file tree
Hide file tree
Showing 20 changed files with 199 additions and 113 deletions.
4 changes: 2 additions & 2 deletions src/main/java/htsjdk/samtools/BAMRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ static boolean isSentinelCigar(final Cigar cigar, final int readLength) {
* extracts the CIGAR from the CG tag and places it into the (in memory) cigar.
*/
private void extractCigarFromCGAttribute(final Cigar sentinelCigar) throws IllegalStateException {
final int[] cigarFromCG = (int[]) getAttribute(SAMTagUtil.getSingleton().CG);
final int[] cigarFromCG = (int[]) getAttribute(SAMTag.CG.getBinaryTag());

if (cigarFromCG == null) return;

Expand Down Expand Up @@ -353,7 +353,7 @@ private void extractCigarFromCGAttribute(final Cigar sentinelCigar) throws Illeg
initializeCigar(decodedCigar);

// remove CG attribute.
setAttribute(SAMTagUtil.getSingleton().CG, null);
setAttribute(SAMTag.CG.getBinaryTag(), null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public SAMBinaryTagAndUnsignedArrayValue(final short tag, final Object value) {
if (!value.getClass().isArray() || value instanceof float[]) {
throw new IllegalArgumentException("Attribute type " + value.getClass() +
" cannot be encoded as an unsigned array. Tag: " +
SAMTagUtil.getSingleton().makeStringTag(tag));
SAMTagUtil.makeStringTag(tag));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/htsjdk/samtools/SAMBinaryTagAndValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public SAMBinaryTagAndValue(final short tag, final Object value) {
}
if (!isAllowedAttributeValue(value)) {
throw new IllegalArgumentException("Attribute type " + value.getClass() + " not supported. Tag: " +
SAMTagUtil.getSingleton().makeStringTag(tag));
SAMTagUtil.makeStringTag(tag));
}
this.tag = tag;
this.value = value;
Expand Down
33 changes: 17 additions & 16 deletions src/main/java/htsjdk/samtools/SAMRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public class SAMRecord implements Cloneable, Locatable, Serializable {
/**
* Tags that are known to need the reverse complement if the read is reverse complemented.
*/
@SuppressWarnings("deprecated")
public static List<String> TAGS_TO_REVERSE_COMPLEMENT = Arrays.asList(SAMTag.E2.name(), SAMTag.SQ.name());

/**
Expand Down Expand Up @@ -868,7 +869,7 @@ protected void initializeCigar(final Cigar cigar) {
* @throws ClassCastException if RG tag does not have a String value.
*/
public SAMReadGroupRecord getReadGroup() {
final String rgId = (String)getAttribute(SAMTagUtil.getSingleton().RG);
final String rgId = (String)getAttribute(SAMTag.RG.getBinaryTag());
if (rgId == null || getHeader() == null) {
return null;
} else {
Expand Down Expand Up @@ -1162,7 +1163,7 @@ public boolean hasAttribute(final String tag) {
* @return Appropriately typed tag value, or null if the requested tag is not present.
*/
public Object getAttribute(final String tag) {
return getAttribute(SAMTagUtil.getSingleton().makeBinaryTag(tag));
return getAttribute(SAMTagUtil.makeBinaryTag(tag));
}

/**
Expand Down Expand Up @@ -1196,7 +1197,7 @@ public Integer getIntegerAttribute(final String tag) {
* @throws {@link htsjdk.samtools.SAMException} if the value is out of range for a 32-bit unsigned value, or not a Number
*/
public Long getUnsignedIntegerAttribute(final String tag) throws SAMException {
return getUnsignedIntegerAttribute(SAMTagUtil.getSingleton().makeBinaryTag(tag));
return getUnsignedIntegerAttribute(SAMTagUtil.makeBinaryTag(tag));
}

/**
Expand All @@ -1219,11 +1220,11 @@ public Long getUnsignedIntegerAttribute(final short tag) throws SAMException {
return lValue;
} else {
throw new SAMException("Unsigned integer value of tag " +
SAMTagUtil.getSingleton().makeStringTag(tag) + " is out of bounds for a 32-bit unsigned integer: " + lValue);
SAMTagUtil.makeStringTag(tag) + " is out of bounds for a 32-bit unsigned integer: " + lValue);
}
} else {
throw new SAMException("Unexpected attribute value data type " + value.getClass() + " for tag " +
SAMTagUtil.getSingleton().makeStringTag(tag));
SAMTagUtil.makeStringTag(tag));
}
}

Expand Down Expand Up @@ -1374,7 +1375,7 @@ public float[] getFloatArrayAttribute(final String tag) {
* @throws SAMException if the tag is not present.
*/
public boolean isUnsignedArrayAttribute(final String tag) {
final SAMBinaryTagAndValue tmp = this.mAttributes.find(SAMTagUtil.getSingleton().makeBinaryTag(tag));
final SAMBinaryTagAndValue tmp = this.mAttributes.find(SAMTagUtil.makeBinaryTag(tag));
if (tmp != null) return tmp.isUnsignedArray();
throw new SAMException("Tag " + tag + " is not present in this SAMRecord");
}
Expand Down Expand Up @@ -1419,7 +1420,7 @@ public Object getAttribute(final short tag) {
* String values are not validated to ensure that they conform to SAM spec.
*/
public void setAttribute(final String tag, final Object value) {
setAttribute(SAMTagUtil.getSingleton().makeBinaryTag(tag), value);
setAttribute(SAMTagUtil.makeBinaryTag(tag), value);
}

/**
Expand All @@ -1435,7 +1436,7 @@ public void setUnsignedArrayAttribute(final String tag, final Object value) {
if (Array.getLength(value) == 0) {
throw new IllegalArgumentException("Empty array passed to setUnsignedArrayAttribute for tag " + tag);
}
setAttribute(SAMTagUtil.getSingleton().makeBinaryTag(tag), value, true);
setAttribute(SAMTagUtil.makeBinaryTag(tag), value, true);
}

/**
Expand Down Expand Up @@ -1556,8 +1557,8 @@ public List<SAMTagAndValue> getAttributes() {
SAMBinaryTagAndValue binaryAttributes = getBinaryAttributes();
final List<SAMTagAndValue> ret = new ArrayList<>();
while (binaryAttributes != null) {
ret.add(new SAMTagAndValue(SAMTagUtil.getSingleton().makeStringTag(binaryAttributes.tag),
binaryAttributes.value));
ret.add(new SAMTagAndValue(SAMTagUtil.makeStringTag(binaryAttributes.tag),
binaryAttributes.value));
binaryAttributes = binaryAttributes.getNext();
}
return ret;
Expand Down Expand Up @@ -1769,8 +1770,8 @@ private void addField(final StringBuilder buffer, final String field) {
buffer.append(field);
}

private String formatTagValue(final short tag, final Object value) {
final String tagString = SAMTagUtil.getSingleton().makeStringTag(tag);
private static String formatTagValue(final short tag, final Object value) {
final String tagString = SAMTagUtil.makeStringTag(tag);
if (value == null || value instanceof String) {
return tagString + ":Z:" + value;
} else if (value instanceof Integer || value instanceof Long ||
Expand Down Expand Up @@ -2062,7 +2063,7 @@ public List<SAMValidationError> isValid(final boolean firstOnly) {
*/
}
// Validate the RG ID is found in header
final String rgId = (String)getAttribute(SAMTagUtil.getSingleton().RG);
final String rgId = (String)getAttribute(SAMTag.RG.getBinaryTag());
if (rgId != null && getHeader() != null && getHeader().getReadGroup(rgId) == null) {
if (ret == null) ret = new ArrayList<>();
ret.add(new SAMValidationError(SAMValidationError.Type.READ_GROUP_NOT_FOUND,
Expand All @@ -2077,10 +2078,10 @@ public List<SAMValidationError> isValid(final boolean firstOnly) {
}
// TODO(mccowan): Is this asking "is this the primary alignment"?
if (this.getReadLength() == 0 && !this.isSecondaryAlignment()) {
final Object fz = getAttribute(SAMTagUtil.getSingleton().FZ);
final Object fz = getAttribute(SAMTag.FZ.getBinaryTag());
if (fz == null) {
final String cq = (String)getAttribute(SAMTagUtil.getSingleton().CQ);
final String cs = (String)getAttribute(SAMTagUtil.getSingleton().CS);
final String cq = (String)getAttribute(SAMTag.CQ.getBinaryTag());
final String cs = (String)getAttribute(SAMTag.CS.getBinaryTag());
if (cq == null || cq.isEmpty() || cs == null || cs.isEmpty()) {
if (ret == null) ret = new ArrayList<>();
ret.add(new SAMValidationError(SAMValidationError.Type.EMPTY_READ,
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public void setHeader(final SAMFileHeader header) {
*/
public void addRecord(final SAMRecord record) {
if (record.getReadPairedFlag() && !record.getMateUnmappedFlag() &&
null == record.getAttribute(SAMTagUtil.getSingleton().MC)) {
null == record.getAttribute(SAMTag.MC.getBinaryTag())) {
throw new SAMException("Mate Cigar tag (MC) not found in: " + record.getReadName());
}
this.records.add(record);
Expand Down
57 changes: 53 additions & 4 deletions src/main/java/htsjdk/samtools/SAMTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,43 @@ public enum SAMTag {
AS,
BC,
BQ,
BZ,
CB,
CC,
CG,
CM,
CO,
CP,
CQ,
CR,
CS,
CT,
CY,
E2,
FI,
FS,
FT,
FZ,
GC, // for backwards compatibility
/** @deprecated for backwards compatibility only */
@Deprecated
GC,
/** @deprecated for backwards compatibility only */
@Deprecated
GS, // for backwards compatibility
GQ, // for backwards compatibility
/** @deprecated for backwards compatibility only */
@Deprecated
GQ,
LB,
H0,
H1,
H2,
HI,
IH,
MC,
MF, // for backwards compatibility
/** @deprecated for backwards compatibility only */
@Deprecated
MF,
MI,
MD,
MQ,
NH,
Expand All @@ -64,20 +77,56 @@ public enum SAMTag {
OC,
OF,
OR,
OX,
PG,
PQ,
PT,
PU,
QT,
Q2,
QX,
R2,
RG,
/**
* @deprecated use BC instead, for backwards compatibilty only
*/
@Deprecated
RT,
RX,
/** @deprecated for backwards compatibility only */
@Deprecated
S2, // for backwards compatibility
SA,
SM,
/** @deprecated for backwards compatibility only */
@Deprecated
SQ, // for backwards compatibility
TC,
U2,
UQ
UQ;

private final short shortValue = SAMTag.makeBinaryTag(this.name());;

/**
* Convert from String representation of tag name to short representation.
*
* @param tag 2-character String representation of a tag name.
* @return Tag name packed as 2 ASCII bytes in a short.
*/
static short makeBinaryTag(String tag) {
if (tag.length() != 2) {
throw new IllegalArgumentException("String tag does not have length() == 2: " + tag);
}
return (short)(tag.charAt(1) << 8 | tag.charAt(0));
}

/**
* Get the binary representation of this tag name.
* @see SAMTagUtil#makeBinaryTag(String)
*
* @return the binary representation of this tag name
*/
public short getBinaryTag(){
return this.shortValue;
}
}
Loading

0 comments on commit 5b897e4

Please sign in to comment.