/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
/**
*
*/
package org.ebayopensource.turmeric.runtime.common.impl.internal.utils;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Pre-reading recorder. On the first read call, will try to pre-read up to <code>maxBytes</code> bytes of data from the inner
* stream. Then, all reads will occur out of that pre-read buffer, until it is exhausted, and reading will continue to the
* inner stream.
*
*/
public class PrereadingRawDataRecorder extends FilterInputStream {
private byte[] m_prereadBuffer; // preread buffer, null if we haven't preread yet
private ByteArrayInputStream m_prereadStream; // preread stream, null if we're done using it (or haven't preread yet)
private int m_length; // number of bytes actually in the preread buffer/stream, could be < max.
private int m_maxBytes; // maximum number of bytes to buffer
public PrereadingRawDataRecorder(InputStream is, int maxBytes) {
super(is);
m_length = 0;
if (maxBytes < 0) {
throw new IllegalArgumentException("Maximum payload bytes must be a positive integer");
}
if (maxBytes > 65536) {
maxBytes = 65536;
}
m_maxBytes = maxBytes;
}
@Override
public int read() throws IOException {
preread();
if (m_prereadStream == null) {
// no more buffer data, delegate to the inner stream in case there is more data beyond the buffered part
return super.read();
}
int ch = m_prereadStream.read();
if (ch == -1) {
// no more buffer data, delegate
m_prereadStream = null;
return super.read();
}
return ch;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
}
preread();
if (m_prereadStream == null) {
// no more buffer data, delegate
return super.read(b, off, len);
}
int bytesRead = m_prereadStream.read(b, off, len);
if (bytesRead == -1) {
m_prereadStream = null;
return super.read(b, off, len);
} else if (bytesRead < len) {
// part of the data came from the buffer, try delegating to inner stream and sum the two return counts.
m_prereadStream = null;
int streamBytesRead = super.read(b, off + bytesRead, len - bytesRead);
if (streamBytesRead == -1) {
return bytesRead;
}
return bytesRead + streamBytesRead;
}
return bytesRead;
}
@Override
public int available() throws IOException {
preread();
if (m_prereadStream == null) {
return super.available();
}
return m_prereadStream.available() + super.available();
}
@Override
public boolean markSupported() {
return false;
}
@Override
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public byte[] getRawByteData() throws IOException {
preread();
byte result[] = new byte[m_length];
System.arraycopy(m_prereadBuffer, 0, result, 0, m_length);
return result;
}
public long getLength() {
return m_length;
}
private void preread() throws IOException {
if (m_prereadBuffer != null) {
return;
}
m_prereadBuffer = new byte[m_maxBytes];
boolean isEof = false;
while (!isEof && m_length < m_maxBytes) {
int bytesRead = super.read(m_prereadBuffer, m_length, m_maxBytes - m_length);
if (bytesRead == -1) {
isEof = true;
} else {
m_length += bytesRead;
}
}
m_prereadStream = new ByteArrayInputStream(m_prereadBuffer, 0, m_length);
}
}