package com.illumina.basespace.igv.vcf; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.logging.Logger; import org.broad.tribble.AbstractFeatureReader; import org.broad.tribble.CloseableTribbleIterator; import org.broad.tribble.Feature; import org.broad.tribble.FeatureCodec; import org.broad.tribble.TribbleException; import org.broad.tribble.index.Index; 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; import com.illumina.basespace.infrastructure.BaseSpaceException; import com.illumina.basespace.param.PositionalQueryParams; /** * * @author bking * */ public class BaseSpaceVariantSetApiFeatureReader extends AbstractFeatureReader { private Index index; private Logger log = Logger.getLogger(BaseSpaceVariantSetApiFeatureReader.class.getPackage().getName()); private VCFTrackLoader locator; public static final long oneDay = 24 * 60 * 60 * 1000; protected BaseSpaceVariantSetApiFeatureReader(VCFTrackLoader locator, FeatureCodec<?> codec) throws IOException { super(null, codec); this.locator = locator; readHeader(); } private static Hashtable<String, Boolean> fileErrors = new Hashtable<String, Boolean>(); static synchronized boolean wasVcfError(String vcfFileId) { return fileErrors.containsKey(vcfFileId) ? fileErrors.get(vcfFileId) : false; } public static synchronized void clearVcfErrors() { fileErrors.clear(); } @Override public List<?> getSequenceNames() { return index == null ? new ArrayList<String>() : new ArrayList<String>(index.getSequenceNames()); } @Override public CloseableTribbleIterator iterator() throws IOException { return new WFIterator(); } @Override public CloseableTribbleIterator query(String chr, int start, int end) throws IOException { return query(chr, start, end, 0); } public CloseableTribbleIterator query(String chr, int start, int end, int scale) throws IOException { fileErrors.remove(locator.getFile().getId()); try { PositionalQueryParams params = new PositionalQueryParams(start, end, 0, 1024); String result = BaseSpaceMain.instance().getApiClient(locator.getClientId()) .getVariantRawRecord(locator.getFile(), chr, params); return new BaseSpaceQueryIterator(result, codec); } catch (BaseSpaceException ex) { // If no data, API returns error code 404 NOT FOUND if (ex.getErrorCode() == 404) { // wasVCFErrror = true; fileErrors.put(locator.getFile().getId(), new Boolean(true)); BaseSpaceMain.logger.warning("Variant query returned 404"); return new BaseSpaceQueryIterator("", codec); } throw ex; } catch (Throwable t) { throw new RuntimeException(t); } } private void readHeader() { InputStream is = null; try { is = BaseSpaceMain.instance().getApiClient(locator.getClientId()).getFileInputStream(locator.getFile()); header = codec.readHeader(new PositionalBufferedStream(is)); } catch (Exception e) { throw new TribbleException.MalformedFeatureFile("Unable to parse header with error: " + e.getMessage(), locator.getFile().getName(), e); } finally { BaseSpaceUtil.dispose(is); } } /** * Class to iteratoe over an entire file. * * @param <T> */ @SuppressWarnings("rawtypes") class WFIterator<T extends Feature> implements CloseableTribbleIterator { private T currentRecord; private LineReader reader; /** * Constructor for iterating over the entire file (seekableStream). * * @throws IOException */ public WFIterator() throws IOException { InputStream is = BaseSpaceMain.instance().getApiClient(locator.getClientId()) .getFileInputStream(locator.getFile()); reader = new AsciiLineReader(is); readNextRecord(); } 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; } /** * Advance to the next record in the query interval. * * @throws IOException */ private void readNextRecord() throws IOException { currentRecord = null; String nextLine; while ((nextLine = reader.readLine()) != null) { // Loop through current block Feature f = null; try { //System.out.println("VCF LINE for " + locator.getPath() + "->" + nextLine); f = codec.decode(new PositionalBufferedStream(new ByteArrayInputStream(nextLine.getBytes()))); if (f == null) { continue; // Skip } currentRecord = (T) f; // Success return; } catch (TribbleException e) { e.setSource(locator.getFile().getName()); throw e; } catch (NumberFormatException e) { String error = "Error parsing line: " + nextLine; throw new TribbleException.MalformedFeatureFile(error, locator.getFile().getName(), e); } } } public void remove() { throw new UnsupportedOperationException("Remove is not supported in Iterators"); } public void close() { reader.close(); } public WFIterator<T> iterator() { return this; } } @Override public void close() throws IOException { } }