/* * The MIT License * * Copyright (c) 2014 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; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * In-memory representation of the binning index for a single reference. BAM and Tabix are both binning indices * with slightly different disk formats but identical in-memory representations. */ public class BinningIndexContent { /** * The reference sequence for the data currently loaded. */ private final int mReferenceSequence; /** * A list of all bins in the above reference sequence. */ private final BinList mBinList; /** * The linear index for the reference sequence above. */ private final LinearIndex mLinearIndex; /** * @param referenceSequence Content corresponds to this reference. * @param binList Array of bins represented by this content, possibly sparse * @param linearIndex Additional index used to optimize queries */ public BinningIndexContent(final int referenceSequence, final BinList binList, final LinearIndex linearIndex) { this.mReferenceSequence = referenceSequence; this.mBinList = binList; this.mLinearIndex = linearIndex; } /** * Reference for this Content */ public int getReferenceSequence() { return mReferenceSequence; } /** * Does this content have anything in this bin? */ public boolean containsBin(final Bin bin) { return mBinList.getBin(bin.getBinNumber()) != null; } /** * @return iterable list of bins represented by this content */ public BinList getBins() { return mBinList; } /** * @return the number of non-null bins represented by this content */ int getNumberOfNonNullBins() { return mBinList.getNumberOfNonNullBins(); } /** * @return all chunks associated with all bins in this content */ public List<Chunk> getAllChunks() { final List<Chunk> allChunks = new ArrayList<Chunk>(); for (final Bin b : mBinList) if (b.getChunkList() != null) { allChunks.addAll(b.getChunkList()); } return Collections.unmodifiableList(allChunks); } /** * @return the linear index represented by this content */ public LinearIndex getLinearIndex() { return mLinearIndex; } /** * * @param startPos 1-based, inclusive * @param endPos 1-based, inclusive * @return List of Chunks overlapping the given region. May return null if there are none. */ public List<Chunk> getChunksOverlapping(final int startPos, final int endPos) { final BitSet overlappingBins = GenomicIndexUtil.regionToBins(startPos,endPos); if (overlappingBins == null) return null; // System.out.println("# Sequence target TID: " + referenceIndex); final List<Chunk> chunkList = new ArrayList<Chunk>(); for (int index = overlappingBins.nextSetBit(0); index >= 0; index = overlappingBins.nextSetBit(index + 1)) { final Bin bin = getBins().getBin(index); if (bin != null) { for (final Chunk chunk : bin.getChunkList()) { chunkList.add(chunk.clone()); } } } if (chunkList.isEmpty()) { return null; } return Chunk.optimizeChunkList(chunkList, getLinearIndex().getMinimumOffset(startPos)); } /** * This class is used to encapsulate the list of Bins store in the BAMIndexContent * While it is currently represented as an array, we may decide to change it to an ArrayList or other structure */ public static class BinList implements Iterable<Bin> { private final Bin[] mBinArray; public final int numberOfNonNullBins; public final int maxBinNumber; // invariant: maxBinNumber = mBinArray.length -1 since array is 0 based /** * @param binArray a sparse array representation of the bins. The index into the array is the bin number. * @param numberOfNonNullBins */ public BinList(final Bin[] binArray, final int numberOfNonNullBins) { this.mBinArray = binArray; this.numberOfNonNullBins = numberOfNonNullBins; this.maxBinNumber = mBinArray.length - 1; } Bin getBin(final int binNumber) { if (binNumber > maxBinNumber) return null; return mBinArray[binNumber]; } int getNumberOfNonNullBins() { return numberOfNonNullBins; } /** * @return An iterator over all non-empty bins. */ public Iterator<Bin> iterator() { return new BinIterator(); } private class BinIterator implements Iterator<Bin> { /** * Stores the bin # of the Bin currently in use. */ private int nextBin; public BinIterator() { nextBin = 0; } /** * Are there more bins in this set, waiting to be returned? * * @return True if more bins are remaining. */ public boolean hasNext() { while (nextBin <= maxBinNumber) { if (getBin(nextBin) != null) return true; nextBin++; } return false; } /** * Gets the next bin in the provided BinList. * * @return the next available bin in the BinList. */ public Bin next() { if (!hasNext()) throw new NoSuchElementException("This BinIterator is currently empty"); final Bin result = getBin(nextBin); nextBin++; return result; } public void remove() { throw new UnsupportedOperationException("Unable to remove from a bin iterator"); } } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final BinList bins = (BinList) o; if (maxBinNumber != bins.maxBinNumber) return false; if (numberOfNonNullBins != bins.numberOfNonNullBins) return false; if (!Arrays.equals(mBinArray, bins.mBinArray)) return false; return true; } @Override public int hashCode() { int result = Arrays.hashCode(mBinArray); result = 31 * result + numberOfNonNullBins; result = 31 * result + maxBinNumber; return result; } } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final BinningIndexContent that = (BinningIndexContent) o; if (mReferenceSequence != that.mReferenceSequence) return false; if (!mBinList.equals(that.mBinList)) return false; if (!mLinearIndex.equals(that.mLinearIndex)) return false; return true; } @Override public int hashCode() { int result = mReferenceSequence; result = 31 * result + mBinList.hashCode(); result = 31 * result + mLinearIndex.hashCode(); return result; } }