package picard.vcf; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CoordMath; import htsjdk.samtools.util.Interval; import htsjdk.samtools.util.IntervalList; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.vcf.VCFFileReader; import java.util.Iterator; /** * Takes a VCFFileReader and an IntervalList and provides a single iterator over all variants in all the intervals. * * @TODO Currently this uses the VCFFileReader.query method - could be useful to make a version of this iterator that uses the .iterator method * * @author Tim Fennell * @author George Grant */ public class ByIntervalListVariantContextIterator implements Iterator<VariantContext> { private final VCFFileReader reader; private final Iterator<Interval> intervals; private CloseableIterator<VariantContext> currentCloseableIterator = null; private Iterator<VariantContext> currentIterator = null; private Interval lastInterval = null; /** * @param reader the source of variants. * @param intervals the intervals to which to restrict variants. */ public ByIntervalListVariantContextIterator(final VCFFileReader reader, final IntervalList intervals) { this.reader = reader; this.intervals = intervals.uniqued().iterator(); this.advance(); } /** Returns true if the variant and interval overlap. */ private boolean overlapsInterval(final VariantContext ctx, final Interval interval) { if (!ctx.getContig().equals(interval.getContig())) return false; else if (CoordMath.overlaps(ctx.getStart(), ctx.getEnd(), interval.getStart(), interval.getEnd())) return true; else return false; } /** If the current iterator is null or exhausted, move to the next interval. */ private void advance() { while ((currentIterator == null || !currentIterator.hasNext()) && this.intervals.hasNext()) { if (currentIterator != null) currentCloseableIterator.close(); final Interval interval = this.intervals.next(); final Interval previousInterval = this.lastInterval; this.currentCloseableIterator = this.reader.query(interval.getContig(), interval.getStart(), interval.getEnd()); this.currentIterator = this.currentCloseableIterator.stream().filter ( ctx -> null == previousInterval || !overlapsInterval(ctx, previousInterval) ).iterator(); this.lastInterval = interval; } } @Override public boolean hasNext() { return (this.currentIterator != null && this.currentIterator.hasNext()); } @Override public VariantContext next() { final VariantContext ctx = this.currentIterator.next(); advance(); return ctx; } @Override public void remove() { throw new UnsupportedOperationException(); } }