/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.edi.sfrm.io; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.spi.AbstractInterruptibleChannel; /** * A ByteBufferInputStream contains an internal buffer that contains direct * or non-direct byte buffer that may be read from the stream.<br><br> * * Creation Date: 24/10/2006 * * @author Twinsen Tsang * @version 1.0.0 * @since 1.0.1 */ public class ByteBufferInputStream extends InputStream { /** * The internal byte buffer. */ protected ByteBuffer buf; /** * The internal file channel. */ private ReadableByteChannel channel; /** * Creates a <code>ByteBufferInputStream</code> * so that it uses <code>buf</code> as its * byte buffer. * The buffer array is not copied. * * @param buf * the input buffer. */ public ByteBufferInputStream(ByteBuffer buffer) { this.buf = buffer; } /** * Returns the number of bytes that can be read from this input * stream without blocking. * The value returned is limit of byte buffer. * which is the number of bytes remaining to be read from the input buffer. * * @return the number of bytes that can be read from the input stream * without blocking. */ public int available() throws IOException { return buf.remaining(); } /** * Returns the readable byte channel object associated with this byte * buffer input stream. * * @return a readable byte channel for this byte buffer. * * @see java.nio.channels.Channels */ public ReadableByteChannel getChannel(){ synchronized (this) { if (channel == null) channel = new ByteBufferChannelImpl(this); return channel; } } /** * Close the stream. */ public void close() throws IOException { buf.clear(); buf.position(0); buf.flip(); buf = null; // Close the channel if exists. if (this.channel != null) if (this.channel.isOpen()) this.channel.close(); } /** * Mark this stream. */ public synchronized void mark(int readlimit) { buf.mark(); } /** * Mark is support by this InputStream. * * @return always true */ public boolean markSupported() { return true; } /** * Reads the next byte of data from this input stream. The value byte is * returned as an int in the range 0 to 255. If no byte is available because * the end of the stream has been reached, the value -1 is returned. */ public int read() throws IOException { if (buf.position() > buf.limit()) return -1; return this.buf.get(); } /** * */ public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /** * */ public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } int pos = buf.position(); int count = buf.limit(); if (pos >= count) return -1; if (pos + len > count) { len = count - pos; } if (len <= 0) { return 0; } buf.get(b, off, len); return len; } /** * Resets the buffer to the marked position. The marked position * is 0 unless another position was marked or an offset was specified * in the constructor. */ public synchronized void reset() throws IOException { this.buf.reset(); } /** * */ public long skip(long n) throws IOException { super.skip(n); int pos = buf.position(); int newPos = buf.position() + (int) n; int count = buf.limit(); if (newPos > count) newPos = count; buf.position(newPos); return newPos - pos; } /** * * Creation Date: 24/10/2006 * * @author Twinsen Tsang * @version 1.0.0 * @since 1.0.1 */ private static class ByteBufferChannelImpl extends AbstractInterruptibleChannel // Not really interruptible implements ReadableByteChannel { ByteBufferInputStream in; private static final int TRANSFER_SIZE = 8192; private byte buf[] = new byte[0]; private Object readLock = new Object(); ByteBufferChannelImpl(ByteBufferInputStream in) { this.in = in; } public int read(ByteBuffer dst) throws IOException { if (dst == null) throw new NullPointerException(); int len = dst.remaining(); int totalRead = 0; int bytesRead = 0; synchronized (readLock) { if (len >= in.buf.remaining() && in.buf.remaining() > 0){ totalRead = in.buf.remaining(); dst.put(in.buf); } while (totalRead < len){ int bytesToRead = Math.min((len - totalRead), TRANSFER_SIZE); if (buf.length < bytesToRead) buf = new byte[bytesToRead]; if ((totalRead > 0) && !(in.available() > 0)) break; // block at most once try { begin(); bytesRead = in.read(buf, 0, bytesToRead); } finally { end(bytesRead > 0); } if (bytesRead < 0) break; else totalRead += bytesRead; dst.put(buf, 0, bytesRead); } if ((bytesRead < 0) && (totalRead == 0)) return -1; } return totalRead; } protected void implCloseChannel() throws IOException { in.close(); } } } ;