diff --git a/public/java/src/org/broadinstitute/sting/gatk/executive/WindowMaker.java b/public/java/src/org/broadinstitute/sting/gatk/executive/WindowMaker.java index cfbce58ee..43ea46002 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/executive/WindowMaker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/executive/WindowMaker.java @@ -10,6 +10,7 @@ import org.broadinstitute.sting.gatk.iterators.LocusIteratorByState; import org.broadinstitute.sting.gatk.iterators.StingSAMIterator; import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.GenomeLocParser; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import java.util.Iterator; import java.util.List; @@ -37,7 +38,7 @@ public class WindowMaker implements Iterable, I /** * The data source for reads. Will probably come directly from the BAM file. */ - private final Iterator sourceIterator; + private final PeekableIterator sourceIterator; /** * Stores the sequence of intervals that the windowmaker should be tracking. @@ -69,7 +70,7 @@ public class WindowMaker implements Iterable, I this.sourceInfo = shard.getReadProperties(); this.readIterator = iterator; - this.sourceIterator = new LocusIteratorByState(iterator,sourceInfo,genomeLocParser,sampleData); + this.sourceIterator = new PeekableIterator(new LocusIteratorByState(iterator,sourceInfo,genomeLocParser,sampleData)); this.intervalIterator = intervals.size()>0 ? new PeekableIterator(intervals.iterator()) : null; } @@ -100,11 +101,6 @@ public class WindowMaker implements Iterable, I */ private final GenomeLoc locus; - /** - * Signal not to advance the iterator because we're currently sitting at the next element. - */ - private boolean atNextElement = false; - public WindowMakerIterator(GenomeLoc locus) { this.locus = locus; advance(); @@ -124,58 +120,45 @@ public class WindowMaker implements Iterable, I public boolean hasNext() { advance(); - return atNextElement; + return currentAlignmentContext != null; } public AlignmentContext next() { - advance(); - if(!atNextElement) throw new NoSuchElementException("WindowMakerIterator is out of elements for this interval."); + if(!hasNext()) throw new NoSuchElementException("WindowMakerIterator is out of elements for this interval."); - // Prepare object state for no next element. + // Consume this alignment context. AlignmentContext toReturn = currentAlignmentContext; currentAlignmentContext = null; - atNextElement = false; // Return the current element. return toReturn; } private void advance() { - // No shard boundaries specified. If currentAlignmentContext has been consumed, grab the next one. - if(locus == null) { - if(!atNextElement && sourceIterator.hasNext()) { - currentAlignmentContext = sourceIterator.next(); - atNextElement = true; - } - return; - } - - // Can't possibly find another element. Skip out early. - if(currentAlignmentContext == null && !sourceIterator.hasNext()) - return; - // Need to find the next element that is not past shard boundaries. If we travel past the edge of // shard boundaries, stop and let the next interval pick it up. - while(sourceIterator.hasNext()) { - // Seed the current alignment context first time through the loop. - if(currentAlignmentContext == null) - currentAlignmentContext = sourceIterator.next(); + while(currentAlignmentContext == null && sourceIterator.hasNext()) { + // Advance the iterator and try again. + AlignmentContext candidateAlignmentContext = sourceIterator.peek(); - // Found a match. - if(locus.containsP(currentAlignmentContext.getLocation())) { - atNextElement = true; + if(locus == null) { + // No filter present. Return everything that LocusIteratorByState provides us. + currentAlignmentContext = sourceIterator.next(); + } + else if(locus.isPast(candidateAlignmentContext.getLocation())) + // Found a locus before the current window; claim this alignment context and throw it away. + sourceIterator.next(); + else if(locus.containsP(candidateAlignmentContext.getLocation())) { + // Found a locus within the current window; claim this alignment context and call it the next entry. + currentAlignmentContext = sourceIterator.next(); + } + else if(locus.isBefore(candidateAlignmentContext.getLocation())) { + // Whoops. Skipped passed the end of the region. Iteration for this window is complete. Do + // not claim this alignment context in case it is part of the next shard. break; } - // Whoops. Skipped passed the end of the region. Iteration for this window is complete. - if(locus.isBefore(currentAlignmentContext.getLocation())) - break; - - // No more elements to examine. Iteration is complete. - if(!sourceIterator.hasNext()) - break; - - // Advance the iterator and try again. - currentAlignmentContext = sourceIterator.next(); + else + throw new ReviewedStingException("BUG: filtering locus does not contain, is not before, and is not past the given alignment context"); } } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/LocusViewTemplate.java b/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/LocusViewTemplate.java index acfefd627..e5cf80826 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/LocusViewTemplate.java +++ b/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/LocusViewTemplate.java @@ -118,6 +118,20 @@ public abstract class LocusViewTemplate extends BaseTest { testReadsInContext(view, shard.getGenomeLocs(), Collections.singletonList(read)); } + @Test + public void readAndLocusOverlapAtLastBase() { + SAMRecord read = buildSAMRecord("chr1", 1, 5); + SAMRecordIterator iterator = new SAMRecordIterator(read); + + Shard shard = new MockLocusShard(genomeLocParser,Collections.singletonList(genomeLocParser.createGenomeLoc("chr1", 5, 5))); + WindowMaker windowMaker = new WindowMaker(shard,genomeLocParser,iterator,shard.getGenomeLocs(),new SampleDataSource()); + WindowMaker.WindowMakerIterator window = windowMaker.next(); + LocusShardDataProvider dataProvider = new LocusShardDataProvider(shard, window.getSourceInfo(), genomeLocParser, window.getLocus(), window, null, null); + LocusView view = createView(dataProvider); + + testReadsInContext(view, shard.getGenomeLocs(), Collections.singletonList(read)); + } + @Test public void readOverlappingStartTest() { SAMRecord read = buildSAMRecord("chr1", 1, 10);