/*
* Copyright 2015-2016 OpenCB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opencb.opencga.storage.alignment;
import org.opencb.biodata.models.alignment.Alignment;
import org.opencb.opencga.storage.alignment.proto.AlignmentProto;
import java.util.*;
/*
* TODO: Make a builder
*/
/**
* Created with IntelliJ IDEA.
* User: jcoll
* Date: 4/7/14
* Time: 7:36 PM
* To change this template use File | Settings | File Templates.
*/
public class AlignmentSummary {
//Only can add an Alignment when it's opened.
private boolean open;
final private int index;
private int defaultFlag;
private int defaultLen;
private String defaultRNext;
private int defaultOverlappedBucket;
private String[] keysArray;
private Map<String, Integer> keysMap;
private Map.Entry<Integer, Object>[] tagsArray;
private Map<Map.Entry<Integer, Object>, Integer> tagsMap;
public static class AlignmentRegionSummaryBuilder {
final private AlignmentSummary summary;
//Histogram for default values.
final private Map<Integer, Integer> flagsMap;
final private Map<Integer, Integer> lenMap;
final private Map<String, Integer> rnextMap;
final private Map<Integer, Integer> overlapMap;
public AlignmentRegionSummaryBuilder(int index) {
summary = new AlignmentSummary(index);
this.flagsMap = new HashMap<>();
this.lenMap = new HashMap<>();
this.rnextMap = new HashMap<>();
this.overlapMap = new HashMap<>();
}
/**
* This function can only be called when summary is OPEN.
*
* @param overlapped
* @return builder
*/
public AlignmentRegionSummaryBuilder addOverlappedBucket(int overlapped) {
Integer f = overlapMap.get(overlapped);
f = f == null ? 1 : f + 1;
overlapMap.put(overlapped, f);
return this;
}
/**
* This function can only be called when summary is OPEN.
*
* @param alignment
* @return builder
*/
public AlignmentRegionSummaryBuilder addAlignment(Alignment alignment) {
if (!summary.open) {
System.out.println("[ERROR] Alignmentregionsummary.addAlignment() can't be called when is closed!");
//TODO jj: Throw exception?
return this;
}
//FLAG
{
Integer f = flagsMap.get(alignment.getFlags());
f = f == null ? 1 : f + 1;
flagsMap.put(alignment.getFlags(), f);
}
//LENGTH
{
Integer l = lenMap.get(alignment.getLength());
l = l == null ? 1 : l + 1;
lenMap.put(alignment.getLength(), l);
}
//MateReferenceName
{
Integer rn = rnextMap.get(alignment.getMateReferenceName());
rn = rn == null ? 1 : rn + 1;
rnextMap.put(alignment.getMateReferenceName(), rn);
}
//Tags / Attributes
int key;
Map.Entry<Integer, Object> tag;
for (Map.Entry<String, Object> entry : alignment.getAttributes().entrySet()) {
//Update key map
if (!summary.keysMap.containsKey(entry.getKey())) {
summary.keysMap.put(entry.getKey(), key = summary.keysMap.size());
} else {
key = summary.keysMap.get(entry.getKey());
}
//Add new map
tag = new HashMap.SimpleEntry<>(key, entry.getValue());
if (!summary.tagsMap.containsKey(tag)) {
summary.tagsMap.put(tag, summary.tagsMap.size());
}
}
return this;
}
public AlignmentSummary build() {
if (summary.open == false) {
return summary;
}
int maxFlags = 0;
for (Map.Entry<Integer, Integer> entry : flagsMap.entrySet()) {
if (entry.getValue() > maxFlags) {
maxFlags = entry.getValue();
summary.defaultFlag = entry.getKey();
}
}
int maxLen = 0;
for (Map.Entry<Integer, Integer> entry : lenMap.entrySet()) {
if (entry.getValue() > maxLen) {
maxLen = entry.getValue();
summary.defaultLen = entry.getKey();
}
}
int maxRNext = 0;
for (Map.Entry<String, Integer> entry : rnextMap.entrySet()) {
if (entry.getValue() > maxRNext) {
maxRNext = entry.getValue();
summary.defaultRNext = entry.getKey();
}
}
int maxOverlap = 0;
for (Map.Entry<Integer, Integer> entry : overlapMap.entrySet()) {
if (entry.getValue() > maxOverlap) {
maxOverlap = entry.getValue();
summary.defaultOverlappedBucket = entry.getKey();
}
}
summary.keysArray = new String[summary.keysMap.size()];
for (Map.Entry<String, Integer> entry : summary.keysMap.entrySet()) {
summary.keysArray[entry.getValue()] = entry.getKey();
}
summary.tagsArray = new Map.Entry[summary.tagsMap.size()];
for (Map.Entry<Map.Entry<Integer, Object>, Integer> entry : summary.tagsMap.entrySet()) {
summary.tagsArray[entry.getValue()] = entry.getKey();
}
summary.open = false;
return summary;
}
public void printHistogram() {
System.out.println("AlignmentRegionSummary [" + summary.index + "] Histogram");
System.out.println("Default Flag Map");
for (Map.Entry<Integer, Integer> entry : flagsMap.entrySet()) {
System.out.print(entry.getKey() + "\t");
for (int i = 0; i < entry.getValue(); i++) System.out.print("*");
System.out.println("");
}
System.out.println("\nDefault Length Map");
for (Map.Entry<Integer, Integer> entry : lenMap.entrySet()) {
System.out.print(entry.getKey() + "\t");
for (int i = 0; i < entry.getValue(); i++) System.out.print("*");
System.out.println("");
}
System.out.println("\nDefault RNext Map");
for (Map.Entry<String, Integer> entry : rnextMap.entrySet()) {
System.out.print(entry.getKey() + "\t");
for (int i = 0; i < entry.getValue(); i++) System.out.print("*");
System.out.println("");
}
System.out.println("\nDefault OverlappedBucket Map");
for (Map.Entry<Integer, Integer> entry : overlapMap.entrySet()) {
System.out.print(entry.getKey() + "\t");
for (int i = 0; i < entry.getValue(); i++) System.out.print("*");
System.out.println("");
}
}
}
private AlignmentSummary(int index) {
this.open = true;
this.index = index;
this.keysMap = new HashMap<>();
this.keysArray = null;
this.tagsMap = new HashMap<>();
this.tagsArray = null;
}
public AlignmentSummary(AlignmentProto.Summary summary, int index) {
this.open = false;
this.index = index;
this.defaultFlag = summary.getDefaultFlag();
this.defaultLen = summary.getDefaultLen();
this.defaultRNext = summary.getDefaultRNext();
this.defaultOverlappedBucket = summary.getDefaultOverlapped();
String keys = summary.getKeys();
this.keysArray = new String[keys.length() / 2];
this.keysMap = new HashMap<>();
for (int i = 0; i < keys.length() / 2; i++) {
keysArray[i] = keys.substring(i * 2, i * 2 + 2);
keysMap.put(keysArray[i], i);
}
Map.Entry<Integer, Object> tag;
Object value = "";
this.tagsMap = new HashMap<>();
this.tagsArray = new Map.Entry[summary.getValuesCount()];
for (AlignmentProto.Summary.Pair pair : summary.getValuesList()) {
if (pair.hasAvalue()) {
value = (char) pair.getAvalue();
} else if (pair.hasFvalue()) {
value = pair.getFvalue();
} else if (pair.hasIvalue()) {
value = pair.getIvalue();
} else if (pair.hasZvalue()) {
value = pair.getZvalue();
}
AbstractMap.SimpleEntry<Integer, Object> entry = new HashMap.SimpleEntry<>(pair.getKey(), value);
tagsArray[tagsMap.size()] = entry;
tagsMap.put(entry, tagsMap.size());
}
}
/**
* This function can only be called when summary is CLOSED.
*/
public AlignmentProto.Summary toProto() {
String keys = "";
for (String s : keysArray) {
keys += s;
}
AlignmentProto.Summary.Pair[] pairsArray = new AlignmentProto.Summary.Pair[tagsMap.size()];
for (Map.Entry<Map.Entry<Integer, Object>, Integer> entry : tagsMap.entrySet()) {
if (pairsArray[entry.getValue()] != null) {
System.out.println("[ERROR] Duplicated tag index.");
}
AlignmentProto.Summary.Pair.Builder builder = AlignmentProto.Summary.Pair.newBuilder();
if (entry.getKey().getValue() instanceof Integer) {
builder.setIvalue((Integer) entry.getKey().getValue());
} else if (entry.getKey().getValue() instanceof Float) {
builder.setFvalue((Float) entry.getKey().getValue());
} else if (entry.getKey().getValue() instanceof Character) {
builder.setAvalue((Character) entry.getKey().getValue());
} else { //if (entry.getKey().getValue() instanceof String) {
builder.setZvalue((String) entry.getKey().getValue());
}
pairsArray[entry.getValue()] = builder
.setKey(entry.getKey().getKey())
.build();
}
return AlignmentProto.Summary.newBuilder()
.setDefaultFlag(defaultFlag)
.setDefaultLen(defaultLen)
.setDefaultOverlapped(defaultOverlappedBucket)
.setDefaultRNext(defaultRNext)
.setKeys(keys)
.addAllValues(Arrays.asList(pairsArray))
.build();
}
/**
* This function can only be called when summary is CLOSED.
* <p>
* TODO jj: Add better algorithm. For 2.0
*/
public ArrayList<Integer> getIndexTagList(Map<String, Object> alignmentTags) {
ArrayList<Integer> indexTagList = new ArrayList<>(alignmentTags.size());
int key;
Map.Entry<Integer, Object> tag;
for (Map.Entry<String, Object> entry : alignmentTags.entrySet()) {
key = keysMap.get(entry.getKey());
tag = new HashMap.SimpleEntry<>(key, entry.getValue());
indexTagList.add(tagsMap.get(tag));
}
return indexTagList;
}
public Map<String, Object> getTagsFromList(List<Integer> indexTagList) {
Map<String, Object> tags = new HashMap<>();
for (Integer i : indexTagList) {
try {
tags.put(keysArray[tagsArray[i].getKey()], tagsArray[i].getValue());
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("Error? summary.getTagsFromList()");
}
}
return tags;
}
/**
* This function can only be called when summary is CLOSED.
*/
public int getDefaultOverlapped() {
return defaultOverlappedBucket;
}
/**
* This function can only be called when summary is CLOSED.
*/
public String getDefaultRNext() {
return defaultRNext;
}
/**
* This function can only be called when summary is CLOSED.
*/
public int getDefaultLen() {
return defaultLen;
}
/**
* This function can only be called when summary is CLOSED.
*/
public int getDefaultFlag() {
return defaultFlag;
}
/**
* This function can only be called when summary is CLOSED.
*/
public int getIndex() {
return index;
}
}