package com.illumina.basespace.igv.vcf; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.samtools.util.BlockCompressedInputStream; import org.broad.tribble.AbstractFeatureReader; import org.broad.tribble.AsciiFeatureCodec; import org.broad.tribble.CloseableTribbleIterator; import org.broad.tribble.Feature; import org.broad.tribble.FeatureCodec; import org.broad.tribble.TribbleException; import org.broad.tribble.readers.AsciiLineReader; import org.broad.tribble.readers.LineReader; import org.broad.tribble.readers.PositionalBufferedStream; import com.illumina.basespace.igv.BaseSpaceMain; import com.illumina.basespace.igv.BaseSpaceUtil; import com.illumina.basespace.igv.vcf.VCFLocatorFactory.VCFTrackLoader; public class BaseSpaceTabixFeatureReader extends AbstractFeatureReader { private BasespaceTabixReader tabixReader; private List<String> sequenceNames; private VCFTrackLoader locator; protected BaseSpaceTabixFeatureReader(VCFTrackLoader locator, FeatureCodec codec) throws IOException { super(null, codec); this.locator = locator; tabixReader = new BasespaceTabixReader(locator); sequenceNames = new ArrayList<String>(tabixReader.mChr2tid.keySet()); readHeader(); } private void readHeader() throws IOException { PositionalBufferedStream is = null; try { is = new PositionalBufferedStream(new BlockCompressedInputStream(BaseSpaceMain.instance() .getApiClient(locator.getClientId()).getFileInputStream(locator.getFile()))); header = codec.readHeader(is); } catch (Exception e) { throw new TribbleException.MalformedFeatureFile("Unable to parse header with error: " + e.getMessage(), locator.getPath(), e); } finally { BaseSpaceUtil.dispose(is); } } public List<String> getSequenceNames() { return sequenceNames; } public CloseableTribbleIterator query(String chr, int start, int end) throws IOException { List<String> mp = getSequenceNames(); if (mp == null) throw new TribbleException.TabixReaderFailure("Unable to find sequence named " + chr + " in the tabix index. ", locator.getPath()); if (!mp.contains(chr)) return new EmptyIterator(); BaseSpaceTabixIteratorLineReader lineReader = new BaseSpaceTabixIteratorLineReader(tabixReader.query( tabixReader.mChr2tid.get(chr), start - 1, end)); return new FeatureIterator(lineReader, start - 1, end); } public CloseableTribbleIterator iterator() throws IOException { final InputStream is = new BlockCompressedInputStream(BaseSpaceMain.instance() .getApiClient(locator.getClientId()).getFileInputStream(locator.getFile())); final PositionalBufferedStream stream = new PositionalBufferedStream(is); final LineReader reader = new AsciiLineReader(stream); return new FeatureIterator(reader, 0, Integer.MAX_VALUE); } public void close() throws IOException { } class FeatureIterator<T extends Feature> implements CloseableTribbleIterator { private T currentRecord; private LineReader lineReader; private int start; private int end; public FeatureIterator(LineReader lineReader, int start, int end) throws IOException { this.lineReader = lineReader; this.start = start; this.end = end; readNextRecord(); } protected void readNextRecord() throws IOException { currentRecord = null; String nextLine; while (currentRecord == null && (nextLine = lineReader.readLine()) != null) { Feature f = null; try { f = ((AsciiFeatureCodec) codec).decode(nextLine); if (f == null) { continue; // Skip } if (f.getStart() > end) { return; // Done } if (f.getEnd() <= start) { continue; // Skip } currentRecord = (T) f; } catch (TribbleException e) { e.setSource(locator.getPath()); throw e; } catch (NumberFormatException e) { String error = "Error parsing line: " + nextLine; throw new TribbleException.MalformedFeatureFile(error, locator.getPath(), e); } } } public boolean hasNext() { return currentRecord != null; } public T next() { T ret = currentRecord; try { readNextRecord(); } catch (IOException e) { throw new RuntimeException("Unable to read the next record, the last record was at " + ret.getChr() + ":" + ret.getStart() + "-" + ret.getEnd(), e); } return ret; } public void remove() { throw new UnsupportedOperationException("Remove is not supported in Iterators"); } public void close() { lineReader.close(); } public Iterator<T> iterator() { return this; } } static class EmptyIterator<T extends Feature> implements CloseableTribbleIterator { public Iterator iterator() { return this; } public boolean hasNext() { return false; } public Object next() { return null; } public void remove() { } @Override public void close() { } } }