package com.robonobo.common.pageio.buffer; import java.io.IOException; import java.io.InputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.robonobo.mina.external.buffer.Page; import com.robonobo.mina.external.buffer.PageBuffer; import com.robonobo.mina.external.buffer.PageBufferListener; /** * Reads the pages in order from a pagebuffer and makes the data available as an * inputstream * * @author macavity */ public class PageBufferInputStream extends InputStream implements PageBufferListener { private static final boolean LOG_PBIS = false; private Log log = LogFactory.getLog(getClass()); private PageBuffer pb; private long nextPageNum = 0; private Page curPage = null; private long markedPageNum; private int markedPagePosition; public PageBufferInputStream(PageBuffer pageBuf) { this.pb = pageBuf; pageBuf.addListener(this); } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public synchronized int read() throws IOException { if(LOG_PBIS) log.debug("PBIS read()"); if (haveReachedEnd()) return -1; if (curPage != null && curPage.getData().remaining() == 0) { curPage = null; nextPageNum++; } if (curPage == null) { try { waitForNextPage(); if (haveReachedEnd()) return -1; } catch (InterruptedException e) { throw new IOException("Interrupted while waiting for page"); } } return curPage.getData().get(); } @Override public synchronized int read(byte[] b, int off, int len) throws IOException { if(LOG_PBIS) log.debug("PBIS read(byte[], "+len+"b)"); int bytesRead = 0; int leftToRead = len; if (haveReachedEnd()) return -1; while (leftToRead > 0 && !haveReachedEnd()) { if (curPage != null && curPage.getData().remaining() == 0) { nextPageNum = curPage.getPageNumber() + 1; curPage = null; } if (curPage == null) { try { waitForNextPage(); if (haveReachedEnd()) return bytesRead; } catch (InterruptedException e) { throw new IOException("Interrupted while waiting for page"); } } int thisRead = (leftToRead < curPage.getData().remaining()) ? leftToRead : curPage.getData().remaining(); curPage.getData().get(b, off + bytesRead, thisRead); bytesRead += thisRead; leftToRead -= thisRead; } return bytesRead; } @Override public long skip(long n) throws IOException { if(LOG_PBIS) log.debug("PBIS skip("+n+")"); long bytesSkipped = 0; long leftToSkip = n; if (haveReachedEnd()) return -1; while(leftToSkip > 0 && !haveReachedEnd()) { if (curPage != null && curPage.getData().remaining() == 0) { nextPageNum = curPage.getPageNumber() + 1; curPage = null; } if (curPage == null) { try { waitForNextPage(); if (haveReachedEnd()) return bytesSkipped; } catch (InterruptedException e) { throw new IOException("Interrupted while waiting for page"); } } int thisSkip = (int) ((leftToSkip < curPage.getData().remaining()) ? leftToSkip : curPage.getData().remaining()); curPage.getData().position(curPage.getData().position() + thisSkip); bytesSkipped += thisSkip; leftToSkip -= thisSkip; } return bytesSkipped; } @Override public boolean markSupported() { return true; } @Override public synchronized void mark(int readlimit) { if(LOG_PBIS) log.debug("PBIS mark: "+readlimit); if (curPage == null) { markedPageNum = nextPageNum; markedPagePosition = 0; } else { markedPageNum = curPage.getPageNumber(); markedPagePosition = curPage.getData().position(); } } @Override public synchronized void reset() throws IOException { if(LOG_PBIS) log.debug("PBIS reset()"); curPage = pb.getPage(markedPageNum); curPage.getData().position(markedPagePosition); } @Override public void close() throws IOException { pb.removeListener(this); } public synchronized void gotPage(PageBuffer pb, long pageNum) { if (pageNum == nextPageNum) notifyAll(); } public synchronized void advisedOfTotalPages(PageBuffer pb) { // Our reader might be waiting on a page that never comes, so wake them // up notifyAll(); } private void waitForNextPage() throws InterruptedException, IOException { while (curPage == null) { if (haveReachedEnd()) return; if (pb.haveGotPage(nextPageNum)) { curPage = pb.getPage(nextPageNum); curPage.getData().position(0); return; } else { synchronized (this) { wait(); } } } } private boolean haveReachedEnd() { boolean result = pb.getTotalPages() >= 0 && nextPageNum >= pb.getTotalPages(); return result; } }