/* * The MIT License (MIT) * * Copyright (c) 2007-2015 Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.broad.igv.sam; //~--- non-JDK imports -------------------------------------------------------- import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMReadGroupRecord; import htsjdk.samtools.SAMRecord; import org.apache.log4j.Logger; import org.broad.igv.feature.genome.Genome; import org.broad.igv.feature.genome.GenomeManager; import org.broad.igv.prefs.Constants; import org.broad.igv.prefs.IGVPreferences; import org.broad.igv.prefs.PreferencesManager; import org.broad.igv.ui.color.ColorUtilities; import java.util.ArrayList; import java.util.List; /** * @author jrobinso */ public class PicardAlignment extends SAMAlignment implements Alignment { private static Logger log = Logger.getLogger(PicardAlignment.class); private static IGVPreferences prefMgr = PreferencesManager.getPreferences(); private static final int READ_PAIRED_FLAG = 0x1; private static final int PROPER_PAIR_FLAG = 0x2; private static final int READ_UNMAPPED_FLAG = 0x4; private static final int MATE_UNMAPPED_FLAG = 0x8; private static final int READ_STRAND_FLAG = 0x10; protected static final int MATE_STRAND_FLAG = 0x20; private static final int FIRST_OF_PAIR_FLAG = 0x40; private static final int SECOND_OF_PAIR_FLAG = 0x80; private static final int NOT_PRIMARY_ALIGNMENT_FLAG = 0x100; private static final int READ_FAILS_VENDOR_QUALITY_CHECK_FLAG = 0x200; private static final int DUPLICATE_READ_FLAG = 0x400; private static final int SUPPLEMENTARY_ALIGNMENT_FLAG = 0x800; private SAMReadGroupRecord readGroupRecord; private int flags; /** * Picard object upon which this PicardAlignment is based */ private SAMRecord record; public PicardAlignment(SAMRecord record) { super(); this.record = record; this.flags = record.getFlags(); String refName = record.getReferenceName(); Genome genome = GenomeManager.getInstance().getCurrentGenome(); this.chr = genome == null ? refName : genome.getCanonicalChrName(refName); // SAMRecord is 1 based inclusive. IGV is 0 based exclusive. this.end = record.getAlignmentEnd(); // might be modified later for soft clipping this.start = record.getAlignmentStart() - 1; // might be modified later for soft clipping if (record.getReadPairedFlag()) { String mateReferenceName = record.getMateReferenceName(); String mateChr = genome == null ? mateReferenceName : genome.getCanonicalChrName(mateReferenceName); this.setMate(new ReadMate(mateChr, record.getMateAlignmentStart() - 1, record.getMateNegativeStrandFlag(), record.getMateUnmappedFlag())); } SAMFileHeader header = record.getHeader(); String keySequence = null; String flowOrder = null; if (header != null) { String readGroup = (String) record.getAttribute("RG"); if (readGroup != null) { this.readGroupRecord = header.getReadGroup(readGroup); if(this.readGroupRecord != null) { keySequence = this.readGroupRecord.getKeySequence(); flowOrder = this.readGroupRecord.getFlowOrder(); } } } Object colorTag = record.getAttribute("YC"); if (colorTag != null) { try { color = ColorUtilities.stringToColor(colorTag.toString(), null); } catch (Exception e) { log.error("Error interpreting color tag: " + colorTag, e); } } setPairOrientation(); setPairStrands(); createAlignmentBlocks(record.getCigarString(), record.getReadBases(), record.getBaseQualities()); } // End constructor /** * @return The SAMRecord which created this PicardAlignment */ public SAMRecord getRecord() { return this.record; } public Object getAttribute(String key) { // SAM alignment tag keys must be of length 2 return key.length() == 2 ? record.getAttribute(key) : (key.equals("TEMPLATE_ORIENTATION") ? pairOrientation : null); } public boolean isFirstOfPair() { return isPaired() && (flags & FIRST_OF_PAIR_FLAG) != 0; } public boolean isSecondOfPair() { return isPaired() && (flags & SECOND_OF_PAIR_FLAG) != 0; } public boolean isDuplicate() { return (flags & DUPLICATE_READ_FLAG) != 0; } public boolean isMapped() { return (flags & READ_UNMAPPED_FLAG) == 0; } public boolean isPaired() { return (flags & READ_PAIRED_FLAG) != 0; } public boolean isProperPair() { return ((flags & READ_PAIRED_FLAG) != 0) && ((flags & PROPER_PAIR_FLAG) != 0); } public boolean isNegativeStrand() { return (flags & READ_STRAND_FLAG) != 0; } @Override public boolean isSupplementary() { return (flags & SUPPLEMENTARY_ALIGNMENT_FLAG) != 0; } public boolean isVendorFailedRead() { return (flags & READ_FAILS_VENDOR_QUALITY_CHECK_FLAG) != 0; } @Override public boolean isPrimary() { return (flags & NOT_PRIMARY_ALIGNMENT_FLAG) == 0; } @Override public String toString() { return record.getSAMString(); } @Override public String getReadName() { return record.getReadName(); } @Override public int getMappingQuality() { return record.getMappingQuality(); } @Override public int getInferredInsertSize() { return record.getInferredInsertSize(); } @Override public String getCigarString() { return record.getCigarString(); } @Override public int getReadLength() { return record.getReadString().length(); } @Override public String getReadSequence() { return record.getReadString(); } @Override public int getAlignmentStart() { return record.getAlignmentStart() - 1; } @Override public int getAlignmentEnd() { return record.getAlignmentEnd(); } protected String getAttributeString(boolean truncate) { // List of tags to skip. Some tags, like MD and SA, are both quite verbose and not easily // interpreted by a human reader. It is best to just hide these tags. The list of tags // to hide is set through the SAM_HIDDEN_TAGS preference. ArrayList<String> tagsToHide = new ArrayList<String>(), tagsHidden = new ArrayList<String>(); String samHiddenTagsPref = prefMgr.get(Constants.SAM_HIDDEN_TAGS); for (String s : (samHiddenTagsPref == null ? "" : samHiddenTagsPref).split("[, ]")) { if (!s.equals("")) { tagsToHide.add(s); } } StringBuffer buf = new StringBuffer(); SAMRecord record = getRecord(); List<SAMRecord.SAMTagAndValue> attributes = record.getAttributes(); if (attributes != null && !attributes.isEmpty()) { for (SAMRecord.SAMTagAndValue tag : attributes) { if (tagsToHide.contains(tag.tag)) { tagsHidden.add(tag.tag); continue; } buf.append("<br>" + tag.tag + " = "); if (tag.value.getClass().isArray()) { // ignore array types buf.append("[not shown]<br>"); continue; } // Break tag final String tagValue = tag.value.toString(); final int maxLength = 70; if (tagValue.length() > maxLength && truncate) { String[] tokens = tagValue.split("<br>"); for (String token : tokens) { if (token.length() > maxLength) { // Insert line breaks String remainder = token; while (remainder.length() > maxLength) { String tmp = remainder.substring(0, maxLength); int spaceIndex = tmp.lastIndexOf(' '); int idx = spaceIndex > 30 ? spaceIndex : maxLength; final String substring = remainder.substring(0, idx); buf.append(substring); buf.append("<br>"); remainder = remainder.substring(idx); } buf.append(remainder); buf.append("<br>"); } else { buf.append(token); buf.append("<br>"); } } } else { buf.append(tagValue); } } if (tagsHidden.size() > 0) { buf.append("<br>Hidden tags: " + String.join(", ", tagsHidden)); } } return buf.toString(); } public String getSample() { return readGroupRecord == null ? null : readGroupRecord.getSample(); } public String getReadGroup() { return readGroupRecord == null ? null : readGroupRecord.getId(); } public String getLibrary() { return readGroupRecord == null ? null : readGroupRecord.getLibrary(); } }