/*
* 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.hadoop.variant.archive.mr;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.opencb.biodata.models.variant.StudyEntry;
import org.opencb.biodata.models.variant.Variant;
import org.opencb.biodata.models.variant.VariantVcfFactory;
import org.opencb.biodata.models.variant.avro.AlternateCoordinate;
import org.opencb.biodata.models.variant.avro.FileEntry;
import org.opencb.biodata.models.variant.avro.VariantType;
import org.opencb.opencga.storage.hadoop.variant.index.VariantTableStudyRow;
import java.util.*;
import java.util.stream.Collectors;
import static htsjdk.variant.vcf.VCFConstants.GENOTYPE_FILTER_KEY;
import static htsjdk.variant.vcf.VCFConstants.GENOTYPE_KEY;
import static org.opencb.biodata.models.variant.VariantVcfFactory.FILTER;
import static org.opencb.biodata.models.variant.VariantVcfFactory.QUAL;
import static org.opencb.biodata.models.variant.avro.VariantType.NO_VARIATION;
/**
* Created by mh719 on 10/06/2016.
*/
public class VariantLocalConflictResolver {
private static VariantPositionRefAltComparator variantPositionRefAltComparator = new VariantPositionRefAltComparator();
public List<Variant> resolveConflicts(List<Variant> variants) {
Map<AlternateWrapper, Variant> altToVar = removeDuplicatedAlts(variants);
// reindex the other way
Map<Variant, List<Variant>> varToAlt = altToVar.entrySet().stream().collect(
Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(e -> e.getKey().getVariant(), Collectors.toList())));
// sort alts by position
NavigableSet<Variant> altSorted = new TreeSet<>(variantPositionRefAltComparator);
altSorted.addAll(altToVar.keySet().stream().map(v -> v.getVariant()).collect(Collectors.toList()));
List<Variant> resolved = new ArrayList<>();
while (!altSorted.isEmpty()) {
Variant altQuery = altSorted.first();
Variant varQuery = altToVar.get(new AlternateWrapper(altQuery));
Set<Variant> altConflictSet = findConflictAlternates(varQuery, altSorted, altToVar, varToAlt);
Set<Variant> varConflicts = new HashSet<>();
altConflictSet.forEach(a -> varConflicts.add(altToVar.get(new AlternateWrapper(a))));
if (varConflicts.isEmpty()) {
throw new IllegalStateException("Variant didn't find itself: " + altQuery);
} else if (varConflicts.size() == 1) {
if (!varConflicts.contains(varQuery)) {
throw new IllegalStateException("Variant didn't find itself, but others: " + varQuery);
}
resolved.add(varQuery);
} else {
Collection<Variant> varResolved = resolve(altConflictSet, varConflicts, altToVar, varToAlt);
resolved.addAll(varResolved);
}
altSorted.removeAll(altConflictSet);
}
return resolved;
}
/**
* Remove a variant, if the same pos:ref:alt is already represented in a SecAlt variant.
*
* @param variants Collection of Alts as Variant objects.
* @return Map Alternate (SecAlt and Alt as variant) to originating Variant.
*/
protected Map<AlternateWrapper, Variant> removeDuplicatedAlts(Collection<Variant> variants) {
// Remove redundant variants (variant with one alt already represented in a SecAlt)
Map<AlternateWrapper, List<Variant>> altToVarList = variants.stream()
.flatMap(v -> expandToVariants(v).stream().map(e -> new ImmutablePair<>(new AlternateWrapper(e), v)))
.collect(Collectors.groupingBy(Pair::getLeft, Collectors.mapping(Pair::getRight, Collectors.toList())));
// reindex the other way
Map<Variant, Set<AlternateWrapper>> varToAlt =
altToVarList.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(v -> new ImmutablePair<>(v, e.getKey())))
.collect(
Collectors.groupingBy(
Pair::getKey,
Collectors.mapping(Pair::getValue, Collectors.toSet())));
Map<AlternateWrapper, Variant> resMap = new HashMap<>();
Set<AlternateWrapper> altBlackList = new HashSet<>();
altToVarList.entrySet().forEach(e -> {
AlternateWrapper key = e.getKey();
if (altBlackList.contains(key)) {
return; // ignore
}
List<Variant> lst = e.getValue();
if (lst.size() > 1) {
// remove exact duplicated call
Set<Set<AlternateWrapper>> duplicatedCheck = new HashSet<>();
List<Variant> uniqCalls = new ArrayList<>();
lst.forEach(v -> {
if (duplicatedCheck.add(varToAlt.get(v))) {
uniqCalls.add(v);
}
});
lst = uniqCalls;
}
// conflict
while (lst.size() > 1) {
// remove calls with no sec-alt
Optional<Variant> any = lst.stream().filter(
v -> isSamePosRefAlt(v, key.variant)
&& v.getStudies().get(0).getSecondaryAlternates().isEmpty()).findAny();
if (any.isPresent()) {
lst.remove(any.get());
continue; // removed one variant with no sec-alt
}
// >1 variant with sec-alt ...
// chose 'best' call, remove other.
Collections.sort(lst, VARIANT_COMP);
lst.forEach(v -> altBlackList.addAll(varToAlt.get(v)));
lst = Collections.singletonList(lst.get(0));
altBlackList.removeAll(varToAlt.get(lst.get(0)));
}
resMap.put(key, lst.get(0));
});
return resMap;
}
protected boolean isSamePosRefAlt(Variant query, Variant v) {
return v.onSameRegion(query)
&& v.getReference().equals(query.getReference())
&& v.getAlternate().equals(query.getAlternate());
}
/**
* Find all Alts overlapping each Alt of found Variants.
* @param varQuery Variant object as in Vcf.
* @param altSorted Alternates (as Variant objects) from all Variants (Alt and SecAlt) sorted by position.
* @param altToVar Alternates to originating Variant object.
* @param varToAlt Variant with a list of Alternates (Alt and SecAlts).
* @return Set of conflicting Alts.
*/
public static Set<Variant> findConflictAlternates(Variant varQuery, NavigableSet<Variant> altSorted,
Map<AlternateWrapper, Variant> altToVar, Map<Variant, List<Variant>> varToAlt) {
// Get ALTs for Variant
List<Variant> altQueryLst = varToAlt.get(varQuery);
Collections.sort(altQueryLst, variantPositionRefAltComparator);
altSorted.headSet(altQueryLst.get(altQueryLst.size() - 1), true);
NavigableSet<Variant> altConflicts = new TreeSet<>(variantPositionRefAltComparator);
altConflicts.addAll(altSorted.headSet(altQueryLst.get(altQueryLst.size() - 1), true));
// While there are items in the sorted ALT list
// OR there are no overlaps anymore
NavigableSet<Variant> remaining = new TreeSet<>(variantPositionRefAltComparator);
remaining.addAll(altSorted.tailSet(altConflicts.last(), false));
while (!remaining.isEmpty()) {
Variant q = remaining.first();
boolean hasOverlap = altConflicts.stream().filter(a -> hasConflictOverlap(a, q)).findAny().isPresent();
if (!hasOverlap) {
break; // END -> no overlaps.
}
altConflicts.add(q);
// Get all ALTs from variant of ALT
List<Variant> qAlts =
varToAlt.get(
altToVar.get(new AlternateWrapper(q)));
Set<Variant> altResolveList = new HashSet<>(qAlts);
// Add everything which are lower (in sorting order) and their possible ALTs from same variant
while (!altResolveList.isEmpty()) {
List<Variant> tmplst = new ArrayList<>(altResolveList);
altResolveList.clear();
for (Variant toResolve : tmplst) {
if (altConflicts.contains(toResolve)) {
continue; //already in list
}
altConflicts.add(toResolve);
// Add all ALTs to the toResolve list, which are not conlicting yet.
altResolveList.addAll(remaining.headSet(toResolve, false).stream()
.flatMap(v -> varToAlt.get(altToVar.get(new AlternateWrapper(v))).stream())
.filter(v -> !altConflicts.contains(v)).collect(Collectors.toSet()));
}
}
altConflicts.addAll(qAlts);
remaining.clear();
remaining.addAll(altSorted.tailSet(altConflicts.last(), false));
}
return altConflicts;
}
/**
* Returns a nonredudant set of variants, where each position of the genome is only covered once.<br>
* Conflicting regions are converted as NO_VARIATION.
*
* @param altConf Collection of Variants with conflicts.
* @return List of Variants
*/
private Collection<Variant> resolve(Set<Variant> altConf, Set<Variant> varConf, Map<AlternateWrapper, Variant> altToVar,
Map<Variant, List<Variant>> varToAlt) {
if (varConf.size() < 2) {
return varConf;
}
// <start,end> pair stream
List<Pair<Integer, Integer>> secAltPairs = buildRegions(altConf);
int min = secAltPairs.stream().mapToInt(p -> p.getLeft()).min().getAsInt();
int max = secAltPairs.stream().mapToInt(p -> p.getRight()).max().getAsInt();
List<Variant> sorted = new ArrayList<>(varConf);
sorted.sort(VARIANT_COMP);
List<Variant> resolved = new ArrayList<>();
List<Variant> misfit = new ArrayList<>();
for (Variant query : sorted) {
if (resolved.isEmpty()) {
resolved.add(query);
} else {
if (!hasAnyConflictOverlapInclSecAlt(resolved, query, varToAlt)) {
resolved.add(query);
} else if (allSameTypeAndGT(resolved, VariantType.NO_VARIATION)) {
List<Variant> collect = resolved.stream().filter(r -> r.overlapWith(query, true))
.collect(Collectors.toList());
collect.add(query);
List<Pair<Integer, Integer>> pairs = buildRegions(collect);
collect.sort(varPositionOrder);
Variant variant = deepCopy(collect.get(0));
int minPos = pairs.stream().mapToInt(p -> p.getLeft()).min().getAsInt();
if (!variant.getStart().equals(minPos)) {
throw new IllegalStateException("Sorting and merging of NO_VARIATOIN regions went wrong: " + query);
}
variant.setEnd(pairs.stream().mapToInt(p -> p.getRight()).max().getAsInt());
variant.setLength((variant.getEnd() - variant.getStart()) + 1);
resolved.clear();
resolved.add(variant);
} else {
// does not fit
misfit.add(query);
}
}
}
if (!misfit.isEmpty()) {
// fit into place (before and after)
fillNoCall(resolved, misfit.get(0), min, max);
}
return resolved;
}
private boolean allSameTypeAndGT(List<Variant> conflicts, Variant query, VariantType type) {
List<Variant> tmp = new ArrayList<>(conflicts.size() + 1);
tmp.addAll(conflicts);
tmp.add(query);
return allSameTypeAndGT(tmp, type);
}
private boolean allSameTypeAndGT(Collection<Variant> conflicts, VariantType type) {
boolean differentType = conflicts.stream().filter(v -> !v.getType().equals(type)).findAny().isPresent();
if (differentType) {
return false;
}
StudyEntry studyEntry = conflicts.stream().findAny().get().getStudies().get(0);
String sample = studyEntry.getSamplesName().stream().findFirst().get();
String gt = studyEntry.getSampleData(sample, GENOTYPE_KEY);
long count = conflicts.stream().filter(v -> v.getType().equals(type)
&& StringUtils.equals(gt, v.getStudies().get(0).getSampleData(sample, GENOTYPE_KEY))).count();
return ((int) count) == conflicts.size();
}
private static VariantPositionComparator varPositionOrder = new VariantPositionComparator();
private static PositionComparator positionOrder = new PositionComparator();
private void fillNoCall(List<Variant> resolved, Variant q, int start, int end) {
// find missing pieces
List<Pair<Integer, Integer>> holes = getMissingRegions(resolved, start, end);
for (Pair<Integer, Integer> h : holes) { // > 1 hole -> need to make copies of variant object
Variant v = deepCopy(q);
changeVariantToNoCall(v, h.getKey(), h.getValue());
resolved.add(v);
}
}
public static List<Pair<Integer, Integer>> getMissingRegions(List<Variant> target, Variant query) {
return getMissingRegions(target, query.getStart(), query.getEnd());
}
public static List<Pair<Integer, Integer>> getMissingRegions(List<Variant> target, int start, int end) {
List<Pair<Integer, Integer>> targetReg = new ArrayList<>(new HashSet<>(buildRegions(target)));
targetReg.sort(positionOrder);
// target.sort(varPositionOrder);
int min = start;
int max = end;
if (max < min) {
// Insertion -> no need for holes
return Collections.emptyList();
}
int minTarget = targetReg.stream().mapToInt(p -> p.getLeft()).min().getAsInt();
int maxTarget = targetReg.stream().mapToInt(p -> p.getRight()).max().getAsInt();
if (maxTarget < minTarget) {
// Insertion -> split query region into two
return Arrays.asList(
new ImmutablePair<>(Math.min(min, maxTarget), Math.max(min, maxTarget)),
new ImmutablePair<>(Math.min(minTarget, max), Math.max(minTarget, max)));
}
// find missing pieces
List<Pair<Integer, Integer>> holes = new ArrayList<>();
for (Pair<Integer, Integer> pair : targetReg) {
if (min > max) {
break; // All holes closed
}
if (max < pair.getLeft()) { // Region ends before or at start of this target
holes.add(new ImmutablePair<>(min, max));
break; // finish
} else if (min > pair.getRight()) {
// No overlap
min = Math.max(min, pair.getRight() + 1);
} else if (min >= pair.getLeft() && max <= pair.getRight()) {
// Full overlap
min = Math.max(min, pair.getRight() + 1); // Reset min to current target end +1
} else if (min < pair.getLeft() && max >= pair.getLeft()) {
// Query overlaps with target start
holes.add(new ImmutablePair<>(min, pair.getLeft() - 1));
min = Math.max(min, pair.getRight() + 1);
} else if (min <= pair.getRight() && max >= pair.getRight()) {
// Query overlaps with target end
min = Math.max(min, pair.getRight() + 1); // Reset min to current target end +1
}
}
// Fill in holes at the end
if (min > maxTarget && min <= max) {
holes.add(new ImmutablePair<>(min, max));
}
return holes;
}
private static List<Pair<Integer, Integer>> buildRegions(Collection<Variant> target) {
return target.stream().map(v -> buildRegions(v)).collect(Collectors.toList());
}
private static Pair<Integer, Integer> buildRegions(Variant v) {
return new ImmutablePair<>(v.getStart(), v.getEnd());
}
public static boolean hasAnyConflictOverlapInclSecAlt(List<Variant> target, Variant query, Map<Variant, List<Variant>> varToAlt) {
return target.stream().filter(v -> hasAnyConflictOverlapInclSecAlt(v, query, varToAlt)).findAny().isPresent();
}
public static boolean hasAnyConflictOverlapInclSecAlt(Variant a, Variant b, Map<Variant, List<Variant>> varToAlt) {
// Check Direct overlap
if (hasConflictOverlap(a, b)) {
return true;
}
// Check AltCoords as well
List<Variant> aList = varToAlt.get(a);
List<Variant> bList = varToAlt.get(b);
if (aList.size() == 1 && bList.size() == 1) {
return false; // No Secondary alternates in the list -> no possible overlaps.
}
if (aList.size() == 1) {
Variant av = aList.get(0);
return bList.stream().filter(bv -> hasConflictOverlap(av, bv)).findAny().isPresent();
}
if (bList.size() == 1) {
Variant bv = bList.get(0);
return aList.stream().filter(av -> hasConflictOverlap(av, bv)).findAny().isPresent();
}
// Search for any overlap between both lists
return aList.stream().filter(av -> bList.stream().filter(bv -> hasConflictOverlap(av, bv)).findAny()
.isPresent()).findAny().isPresent();
}
/**
* Creates a list with the provided Variant and all secondary alternates {@link AlternateCoordinate} converted to
* Variants.
* @param v {@link Variant}
* @return List of Variant positions.
*/
public static List<Variant> expandToVariants(Variant v) {
Variant nv = asVariant(v);
if (v.getStudies().isEmpty()) {
return Collections.singletonList(nv);
}
StudyEntry studyEntry = v.getStudies().get(0);
List<AlternateCoordinate> secAlt = studyEntry.getSecondaryAlternates();
if (secAlt.isEmpty()) {
return Collections.singletonList(nv);
}
// Check AltCoords as well
List<Variant> list = new ArrayList<>(Collections.singletonList(nv));
secAlt.forEach(alt -> list.add(asVariant(v, alt)));
return list;
}
private static boolean hasConflictOverlap(Variant a, Variant b) {
boolean conflict = a.overlapWith(b, true);
if (conflict && (isInsertion(a) || isInsertion(b))) {
// in case of insertions
if (isInsertion(a) != isInsertion(b)) { // one of them insertion
conflict = isInsertion(a) ? isInsertionCovered(a, b) : isInsertionCovered(b, a);
}
}
return conflict;
}
private static boolean isInsertion(Variant variant) {
return variant.getStart() > variant.getEnd();
}
private static boolean isInsertionCovered(Variant insertion, Variant notInsertion) {
if (!isInsertion(insertion)) {
throw new IllegalStateException("Variable insertion is not an Insertion:" + insertion);
}
if (isInsertion(notInsertion)) {
throw new IllegalStateException("Variable notInsertion is an Insertion:" + notInsertion);
}
Integer start = notInsertion.getStart();
Integer end = notInsertion.getEnd();
return start <= insertion.getStart() && insertion.getStart() <= end
&& start <= insertion.getEnd() && insertion.getEnd() <= end;
}
public static Variant asVariant(Variant v) {
Variant variant =
new Variant(v.getChromosome(), v.getStart(), v.getEnd(), v.getReference(), v.getAlternate(), v.getStrand());
variant.setType(v.getType());
return variant;
}
public static Variant asVariant(Variant a, AlternateCoordinate altA) {
String chr = ObjectUtils.firstNonNull(altA.getChromosome(), a.getChromosome());
Integer start = ObjectUtils.firstNonNull(altA.getStart(), a.getStart());
Integer end = ObjectUtils.firstNonNull(altA.getEnd(), a.getEnd());
String ref = ObjectUtils.firstNonNull(altA.getReference(), a.getReference());
String alt = ObjectUtils.firstNonNull(altA.getAlternate(), a.getAlternate());
VariantType type = ObjectUtils.firstNonNull(altA.getType(), a.getType());
try {
Variant variant = new Variant(chr, start, end, ref, alt);
variant.setType(type);
return variant;
} catch (IllegalArgumentException e) {
String msg = altA + "\n" + a.toJson() + "\n";
throw new IllegalStateException(msg, e);
}
}
public static int min(List<Variant> target) {
return target.stream().mapToInt(v -> v.getStart().intValue()).min().getAsInt();
}
public static int max(List<Variant> target) {
return target.stream().mapToInt(v -> v.getEnd().intValue()).max().getAsInt();
}
public static String extractGenotypeFilter(Variant a) {
List<StudyEntry> studies = a.getStudies();
if (studies.isEmpty()) {
return null;
}
StudyEntry studyEntry = studies.get(0);
List<List<String>> samplesData = studyEntry.getSamplesData();
if (samplesData == null || samplesData.isEmpty()) {
return null;
}
Integer keyPos = studyEntry.getFormatPositions().get(GENOTYPE_FILTER_KEY);
if (null == keyPos) {
return null;
}
List<String> sample = samplesData.get(0);
if (sample.isEmpty()) {
return null;
}
String af = sample.get(keyPos);
return af;
}
public static String extractFileAttribute(Variant a, String key) {
List<StudyEntry> studies = a.getStudies();
if (studies.isEmpty()) {
return null;
}
StudyEntry studyEntry = studies.get(0);
List<FileEntry> files = studyEntry.getFiles();
if (files == null || files.isEmpty()) {
return null;
}
return files.get(0).getAttributes().get(key);
}
/**
* Checks if SiteConflict is part of the Filter flag.
*
* @param a Variant
* @param b Variant
* @return -1 if in a, 1 if in b, 0 if in both or non.
*/
public static int checkSiteConflict(Variant a, Variant b) {
String af = extractFileAttribute(a, FILTER);
String bf = extractFileAttribute(b, FILTER);
return checkStringConflict(af, bf, "SiteConflict");
}
/**
* Checks PASS is the filter flag.
*
* @param a Variant
* @param b Variant
* @return -1 if in a, 1 if in b, 0 if in both or non.
*/
public static int checkPassConflict(Variant a, Variant b) {
String af = ObjectUtils.firstNonNull(extractFileAttribute(a, FILTER), extractGenotypeFilter(a));
String bf = ObjectUtils.firstNonNull(extractFileAttribute(b, FILTER), extractGenotypeFilter(b));
return checkStringConflict(af, bf, "PASS");
}
/**
* Checks Quality score.
*
* @param a Variant
* @param b Variant
* @return -1 if lower in a, 1 if lower in b, 0 if in both are the same or dot.
*/
public static int checkQualityScore(Variant a, Variant b) {
String af = extractFileAttribute(a, QUAL);
String bf = extractFileAttribute(b, QUAL);
Double va = StringUtils.isBlank(af) || StringUtils.equals(af, ".") ? 0d : Double.valueOf(af);
Double vb = StringUtils.isBlank(bf) || StringUtils.equals(bf, ".") ? 0d : Double.valueOf(bf);
return va.compareTo(vb);
}
public static int checkStringConflict(String a, String b, String query) {
if (!(StringUtils.contains(a, query) && StringUtils.contains(b, query))) {
if (StringUtils.contains(a, query)) {
return -1;
}
if (StringUtils.contains(b, query)) {
return 1;
}
}
return 0;
}
private static final VariantComparator VARIANT_COMP = new VariantComparator();
private static class VariantComparator implements Comparator<Variant> {
/**
* Sorts variants by.
* <ul>
* <li>PASS gets preference</li>
* <li>SiteConflict less priority</li>
* <li>Higher quality score first</li>
* <li>Variant before reference block</li>
* <li>Earlier start</li>
* <li>Earlier end</li>
* <li>Hash code</li>
* </ul>
*
* @param o1 Variant
* @param o2 Variant
* @return int
*/
@Override
public int compare(Variant o1, Variant o2) {
// Variant before reference block
if (o1.getType().equals(NO_VARIATION)) {
if (!o2.getType().equals(NO_VARIATION)) {
return 1;
}
} else if (o2.getType().equals(NO_VARIATION)) {
return -1;
}
// Check for PASS
int c = checkPassConflict(o1, o2);
if (c != 0) {
return c;
}
// Check SiteConflict
c = checkSiteConflict(o1, o2) * -1; // invert result
if (c != 0) {
return c;
}
// Check Quality scores
c = checkQualityScore(o1, o2) * -1; // invert result - higher score better
if (c != 0) {
return c;
}
c = o1.getStart().compareTo(o2.getStart());
if (c != 0) {
return c;
}
c = o1.getEnd().compareTo(o2.getEnd());
if (c != 0) {
return c;
}
return Integer.compare(o1.hashCode(), o2.hashCode());
}
}
private static class VariantPositionComparator implements Comparator<Variant> {
@Override
public int compare(Variant o1, Variant o2) {
int c = o1.getStart().compareTo(o2.getStart());
if (c != 0) {
return c;
}
c = o1.getEnd().compareTo(o2.getEnd());
if (c != 0) {
return c;
}
return Integer.compare(o1.hashCode(), o2.hashCode());
}
}
private static class VariantPositionRefAltComparator implements Comparator<Variant> {
@Override
public int compare(Variant o1, Variant o2) {
int c = o1.getStart().compareTo(o2.getStart());
if (c != 0) {
return c;
}
c = o1.getEnd().compareTo(o2.getEnd());
if (c != 0) {
return c;
}
c = o1.getReference().compareTo(o2.getReference());
if (c != 0) {
return c;
}
c = o1.getAlternate().compareTo(o2.getAlternate());
if (c != 0) {
return c;
}
c = o1.getType().compareTo(o2.getType());
if (c != 0) {
return c;
}
return Integer.compare(o1.hashCode(), o2.hashCode());
}
}
private static class PositionComparator implements Comparator<Pair<Integer, Integer>> {
@Override
public int compare(Pair<Integer, Integer> o1, Pair<Integer, Integer> o2) {
int c = o1.getLeft().compareTo(o2.getLeft());
if (c != 0) {
return c;
}
c = o1.getRight().compareTo(o2.getRight());
if (c != 0) {
return c;
}
return Integer.compare(o1.hashCode(), o2.hashCode());
}
}
public static Variant deepCopy(Variant var) {
Variant v = new Variant(var.getChromosome(), var.getStart(), var.getEnd(), var.getReference(), var
.getAlternate());
v.setType(var.getType());
v.setIds(var.getIds());
v.setStrand(var.getStrand());
v.setAnnotation(var.getAnnotation());
for (StudyEntry vse : var.getStudies()) {
StudyEntry se = new StudyEntry();
se.setStudyId(vse.getStudyId());
if (null != vse.getSamplesPosition()) {
se.setSamplesPosition(new HashMap<>(vse.getSamplesPosition()));
} else {
se.setSamplesPosition(new HashMap<>());
}
if (null != vse.getFormat()) {
se.setFormat(new ArrayList<>(vse.getFormat()));
} else {
se.setFormat(new ArrayList<>());
}
List<FileEntry> files = new ArrayList<>(vse.getFiles().size());
for (FileEntry file : vse.getFiles()) {
HashMap<String, String> attributes = new HashMap<>(file.getAttributes()); //TODO: Check file attributes
files.add(new FileEntry(file.getFileId(), file.getCall(), attributes));
}
se.setFiles(files);
int samplesSize = vse.getSamplesData().size();
List<List<String>> newSampleData = new ArrayList<>(samplesSize);
for (int i = 0; i < samplesSize; i++) {
List<String> sd = vse.getSamplesData().get(i);
newSampleData.add(new ArrayList<>(sd));
}
se.setSamplesData(newSampleData);
v.addStudyEntry(se);
}
return v;
}
public static void changeVariantToNoCall(Variant var, Integer start, Integer end) {
var.setReference("");
var.setAlternate("");
var.setStart(start);
var.setEnd(end);
String genotype = VariantTableStudyRow.NOCALL;
var.setType(NO_VARIATION);
StudyEntry se = var.getStudies().get(0);
Map<String, Integer> formatPositions = se.getFormatPositions();
int gtpos = formatPositions.get(GENOTYPE_KEY);
int filterPos = formatPositions.containsKey(GENOTYPE_FILTER_KEY)
? formatPositions.get(GENOTYPE_FILTER_KEY) : -1;
List<List<String>> sdLst = se.getSamplesData();
List<List<String>> oLst = new ArrayList<>(sdLst.size());
for (List<String> sd : sdLst) {
List<String> o = new ArrayList<>(sd);
o.set(gtpos, genotype);
if (filterPos != -1) {
o.set(filterPos, "SiteConflict");
}
oLst.add(o);
}
se.setSamplesData(oLst);
se.setSecondaryAlternates(new ArrayList<>());
for (FileEntry fe : se.getFiles()) {
Map<String, String> feAttr = fe.getAttributes();
if (null == feAttr) {
feAttr = new HashMap<>();
} else {
feAttr = new HashMap<>(feAttr);
}
feAttr.put(VariantVcfFactory.FILTER, "SiteConflict");
fe.setAttributes(feAttr);
}
}
public static class AlternateWrapper {
private final Variant variant;
public AlternateWrapper(Variant variant) {
this.variant = variant;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof AlternateWrapper)) {
return false;
}
AlternateWrapper that = (AlternateWrapper) o;
if (variant == null) {
if (that.variant == null) {
return true;
}
return false;
}
return variantPositionRefAltComparator.compare(variant, that.variant) == 0;
}
@Override
public int hashCode() {
return variant != null ? variant.toString().hashCode() : 0;
}
public Variant getVariant() {
return variant;
}
}
}