/*
* 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.ga4gh;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import htsjdk.samtools.Cigar;
import htsjdk.samtools.TextCigarCodec;
import org.apache.log4j.Logger;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.feature.genome.GenomeManager;
import org.broad.igv.sam.ReadMate;
import org.broad.igv.sam.SAMAlignment;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Created by jrobinso on 6/17/14.
* <p/>
* id
*/
public class Ga4ghAlignment extends SAMAlignment {
private static Logger log = Logger.getLogger(Ga4ghAlignment.class);
private final Map<String, String> tags;
int inferredInsertSize;
int mappingQuality = 255; // 255 by default
String readName;
protected String readSequence;
private boolean negativeStrand;
private int readNumber;
private boolean duplicateFragment;
private int numberReads;
private boolean properPlacement;
private boolean supplementaryAlignment;
private boolean failedVendorQualityChecks;
private boolean secondaryAlignment;
private String cigarString;
private boolean mapped;
public Ga4ghAlignment(JsonObject json) {
Genome genome = GenomeManager.getInstance().getCurrentGenome();
this.readName = json.get("fragmentName").getAsString();
this.properPlacement = hasNonNullValue(json, "properPlacement") ? json.get("properPlacement").getAsBoolean() : true;
this.duplicateFragment = hasNonNullValue(json, "duplicateFragment") ? json.get("duplicateFragment").getAsBoolean() : false;
this.numberReads = hasNonNullValue(json, "numberReads") ? json.get("numberReads").getAsInt() : 1;
this.inferredInsertSize = hasNonNullValue(json, "fragmentLength") ? json.get("fragmentLength").getAsInt() : 0;
this.readNumber = hasNonNullValue(json, "readNumber") ? json.get("readNumber").getAsInt() : 0;
this.failedVendorQualityChecks = hasNonNullValue(json, "failedVendorQualityChecks") ? json.get("failedVendorQualityChecks").getAsBoolean() : false;
JsonObject alignmentObject = json.getAsJsonObject("alignment");
if (alignmentObject == null) {
this.mapped = false;
} else {
this.mapped = true;
JsonObject positionObject = alignmentObject.getAsJsonObject("position");
String refName = positionObject.get("referenceName").getAsString();
this.setChr(genome == null ? refName : genome.getCanonicalChrName(refName));
this.alignmentStart = positionObject.get("position").getAsInt();
this.mappingQuality = hasNonNullValue(alignmentObject, "mappingQuality") ? alignmentObject.get("mappingQuality").getAsInt() : 256;
this.negativeStrand = hasNonNullValue(positionObject, "reverseStrand") && positionObject.get("reverseStrand").getAsBoolean();
this.cigarString = generateCigarString(alignmentObject.getAsJsonArray("cigar"));
this.start = this.alignmentStart; // might be modified later for soft clipping
this.alignmentEnd = this.alignmentStart + getReferenceLength(cigarString);
this.end = alignmentEnd;
}
this.secondaryAlignment = hasNonNullValue(json, "secondaryAlignment") ? json.get("secondaryAlignment").getAsBoolean() : false;
this.supplementaryAlignment = hasNonNullValue(json, "supplementaryAlignment") ? json.get("supplementaryAlignment").getAsBoolean() : false;
this.readSequence = hasNonNullValue(json, "alignedSequence") ? json.get("alignedSequence").getAsString() : null;
byte[] baseQualities = hasNonNullValue(json, "alignedQuality") ? generateBaseQualities(json.getAsJsonArray("alignedQuality")) : null;
JsonObject mateObject = json.getAsJsonObject("nextMatePosition");
if (mateObject == null) {
this.setMate(new ReadMate("*", 0, false, true));
} else {
String mateReferenceName = mateObject.get("referenceName").getAsString();
String mateChr = genome == null ? mateReferenceName : genome.getCanonicalChrName(mateReferenceName);
int matePosition = Integer.parseInt(mateObject.get("position").getAsString());
boolean mateNegStrand = hasNonNullValue(mateObject, "reverseStrand") && mateObject.get("reverseStrand").getAsBoolean();
this.setMate(new ReadMate(mateChr,
matePosition,
mateNegStrand,
false)); // Assuming mate is mapped
}
JsonObject infoObject = json.getAsJsonObject("info");
this.tags = generateTags(infoObject);
setPairOrientation();
setPairStrands();
createAlignmentBlocks(this.cigarString, this.readSequence.getBytes(), baseQualities);
}
public static boolean hasNonNullValue(JsonObject json, String name) {
return json.has(name) && !json.get(name).isJsonNull();
}
private Map<String, String> generateTags(JsonObject infoObject) {
Map<String, String> tags = new HashMap<String, String>();
if (infoObject != null) {
for (Map.Entry<String, JsonElement> entry : infoObject.entrySet()) {
String key = entry.getKey();
JsonArray valueArray = entry.getValue().getAsJsonArray();
String value = valueArray.get(0).getAsString();
for (int i = 1; i < valueArray.size(); i++) {
value += "," + valueArray.get(i).getAsString();
}
tags.put(key, value);
}
}
return tags;
}
private byte[] generateBaseQualities(JsonArray alignedQuality) {
byte[] baseQualities = new byte[alignedQuality.size()];
Iterator<JsonElement> iter = alignedQuality.iterator();
int i = 0;
while (iter.hasNext()) {
baseQualities[i++] = iter.next().getAsByte();
}
return baseQualities;
}
private String generateCigarString(JsonArray cigar) {
StringBuffer cigarStr = new StringBuffer();
Iterator<JsonElement> iter = cigar.iterator();
while (iter.hasNext()) {
JsonObject op = iter.next().getAsJsonObject();
cigarStr.append(op.getAsJsonPrimitive("operationLength").getAsString());
cigarStr.append(CigarMap.get(op.getAsJsonPrimitive("operation").getAsString()));
}
return cigarStr.toString();
}
/**
* @return the unclippedStart
*/
public int getAlignmentStart() {
return alignmentStart;
}
public int getAlignmentEnd() {
return alignmentEnd;
}
public String getReadName() {
return readName;
}
public int getMappingQuality() {
return mappingQuality;
}
public int getInferredInsertSize() {
return inferredInsertSize;
}
public String getCigarString() {
return cigarString;
}
public int getReadLength() {
return readSequence.length();
}
public String getReadSequence() {
return readSequence;
}
@Override
protected String getAttributeString(boolean truncate) {
StringBuffer buffer = new StringBuffer();
for (Map.Entry<String, String> entry : tags.entrySet()) {
buffer.append("<br>");
buffer.append(entry.getKey() + ": " + entry.getValue());
}
return buffer.toString();
}
@Override
public boolean isFirstOfPair() {
return readNumber == 0;
}
@Override
public boolean isSecondOfPair() {
return readNumber > 0;
}
@Override
public boolean isDuplicate() {
return duplicateFragment;
}
@Override
public boolean isMapped() {
return mapped;
}
@Override
public boolean isPaired() {
return numberReads > 1;
}
@Override
public boolean isProperPair() {
return properPlacement;
}
@Override
public boolean isSupplementary() {
return supplementaryAlignment;
}
@Override
public boolean isVendorFailedRead() {
return failedVendorQualityChecks;
}
@Override
public boolean isPrimary() {
return !secondaryAlignment;
}
@Override
public Object getAttribute(String key) {
return tags.get(key);
}
public int getReferenceLength(String cigarString) {
// Use htsjdk class for now
TextCigarCodec codec = new TextCigarCodec();
Cigar cigar = codec.decode(cigarString);
return cigar.getReferenceLength();
}
public boolean isNegativeStrand() {
return negativeStrand;
}
static Map<String, String> CigarMap = new HashMap<String, String>();
static {
CigarMap.put("ALIGNMENT_MATCH", "M");
CigarMap.put("INSERT", "I");
CigarMap.put("DELETE", "D");
CigarMap.put("SKIP", "N");
CigarMap.put("CLIP_SOFT", "S");
CigarMap.put("CLIP_HARD", "H");
CigarMap.put("PAD", "P");
CigarMap.put("SEQUENCE_MATCH", "=");
CigarMap.put("SEQUENCE_MISMATCH", "X");
}
// ReadGroup properties not currently supported for GA4GH
public String getSample() {
return null;
}
public String getReadGroup() {
return null;
}
public String getLibrary() {
return null;
}
;
}