/*
* The MIT License
*
* Copyright (c) 2010 The 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 htsjdk.samtools.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utility class to efficiently do in memory overlap detection between a large
* set of mapping like objects, and one or more candidate mappings.
*/
public class OverlapDetector<T> {
private Map<Object, IntervalTree<Set<T>>> cache = new HashMap<Object, IntervalTree<Set<T>>>();
private final int lhsBuffer;
private final int rhsBuffer;
/**
* Constructs an overlap detector.
* @param lhsBuffer the amount by which to "trim" coordinates of mappings on the left
* hand side when calculating overlaps
* @param rhsBuffer the amount by which to "trim" coordinates of mappings on the right
* hand side when calculating overlaps
*/
public OverlapDetector(int lhsBuffer, int rhsBuffer) {
this.lhsBuffer = lhsBuffer;
this.rhsBuffer = rhsBuffer;
}
/** Adds a mapping to the set of mappings against which to match candidates. */
public void addLhs(T object, Interval interval) {
Object seqId = interval.getSequence();
IntervalTree<Set<T>> tree = this.cache.get(seqId);
if (tree == null) {
tree = new IntervalTree<Set<T>>();
this.cache.put(seqId, tree);
}
int start = interval.getStart() + this.lhsBuffer;
int end = interval.getEnd() - this.lhsBuffer;
Set<T> objects = new HashSet<T>();
objects.add(object);
if (start <= end) // Don't put in sequences that have no overlappable bases
{
Set<T> alreadyThere = tree.put(start, end, objects);
if (alreadyThere != null)
{
alreadyThere.add(object);
tree.put(start, end, alreadyThere);
}
}
}
/** Adds all items to the overlap detector. */
public void addAll(List<T> objects, List<Interval> intervals) {
if (objects.size() != intervals.size()) {
throw new IllegalArgumentException("Objects and intervals must be the same size.");
}
for (int i=0; i<objects.size(); ++i) {
addLhs(objects.get(i), intervals.get(i));
}
}
/** Gets all the objects that could be returned by the overlap detector. */
public Collection<T> getAll() {
Collection<T> all = new HashSet<T>();
for (IntervalTree<Set<T>> tree : this.cache.values()) {
for (IntervalTree.Node<Set<T>> node : tree) {
all.addAll(node.getValue());
}
}
return all;
}
/** Gets the collection of objects that overlap the provided mapping. */
public Collection<T> getOverlaps(Interval rhs) {
Collection<T> matches = new ArrayList<T>();
Object seqId = rhs.getSequence();
IntervalTree<Set<T>> tree = this.cache.get(seqId);
int start = rhs.getStart() + this.rhsBuffer;
int end = rhs.getEnd() - this.rhsBuffer;
if (tree != null && start <= end)
{
Iterator<IntervalTree.Node<Set<T>>> it = tree.overlappers(start, end);
while (it.hasNext())
{
IntervalTree.Node<Set<T>> node = it.next();
matches.addAll(node.getValue());
}
}
return matches;
}
}