package com.ctc.wstx.io;
import java.io.IOException;
import java.io.Reader;
import javax.xml.stream.XMLStreamException;
import com.ctc.wstx.api.ReaderConfig;
import com.ctc.wstx.exc.WstxException;
Input source that reads input via a Reader.
/**
* Input source that reads input via a Reader.
*/
public class ReaderSource
extends BaseInputSource
{
final ReaderConfig mConfig;
Underlying Reader to read character data from
/**
* Underlying Reader to read character data from
*/
protected Reader mReader;
If true, will close the underlying Reader when this source is closed;
if false will leave it open.
/**
* If true, will close the underlying Reader when this source is closed;
* if false will leave it open.
*/
final boolean mDoRealClose;
int mInputProcessed = 0;
int mInputRow = 1;
int mInputRowStart = 0;
public ReaderSource(ReaderConfig cfg, WstxInputSource parent, String fromEntity,
String pubId, SystemId sysId,
Reader r, boolean realClose)
{
super(parent, fromEntity, pubId, sysId);
mConfig = cfg;
mReader = r;
mDoRealClose = realClose;
int bufSize = cfg.getInputBufferLength();
mBuffer = cfg.allocFullCBuffer(bufSize);
}
Method called to change the default offsets this source has. Generally
done when the underlying Reader had been partially read earlier (like
reading the xml declaration before starting real parsing).
/**
* Method called to change the default offsets this source has. Generally
* done when the underlying Reader had been partially read earlier (like
* reading the xml declaration before starting real parsing).
*/
public void setInputOffsets(int proc, int row, int rowStart)
{
mInputProcessed = proc;
mInputRow = row;
mInputRowStart = rowStart;
}
Input location is easy to set, as we'll start from the beginning
of a File.
/**
* Input location is easy to set, as we'll start from the beginning
* of a File.
*/
@Override
protected void doInitInputLocation(WstxInputData reader)
{
reader.mCurrInputProcessed = mInputProcessed;
reader.mCurrInputRow = mInputRow;
reader.mCurrInputRowStart = mInputRowStart;
}
This is a hard-coded assumption, for now this source is
only created from external entities
/**
* This is a hard-coded assumption, for now this source is
* only created from external entities
*/
@Override
public boolean fromInternalEntity() {
return false;
}
@Override
public int readInto(WstxInputData reader)
throws IOException, XMLStreamException
{
/* Shouldn't really try to read after closing, but it may be easier
* for caller not to have to keep track of closure...
*/
if (mBuffer == null) {
return -1;
}
int count = mReader.read(mBuffer, 0, mBuffer.length);
if (count < 1) {
/* Let's prevent caller from accidentally being able to access
* data, first.
*/
mInputLast = 0;
reader.mInputPtr = 0;
reader.mInputEnd = 0;
if (count == 0) {
/* Sanity check; should never happen with correctly written
* Readers:
*/
throw new WstxException("Reader (of type "+mReader.getClass().getName()+") returned 0 characters, even when asked to read up to "+mBuffer.length, getLocation());
}
return -1;
}
reader.mInputBuffer = mBuffer;
reader.mInputPtr = 0;
mInputLast = count;
reader.mInputEnd = count;
return count;
}
@Override
public boolean readMore(WstxInputData reader, int minAmount)
throws IOException, XMLStreamException
{
/* Shouldn't really try to read after closing, but it may be easier
* for caller not to have to keep track of closure...
*/
if (mBuffer == null) {
return false;
}
int ptr = reader.mInputPtr;
int currAmount = mInputLast - ptr;
// Let's first adjust caller's data appropriately:
/* Since we are essentially removing 'ptr' chars that we
* have used already, they count as past chars. Also, since
* offsets are reduced by 'ptr', need to adjust linefeed offset
* marker as well.
*/
reader.mCurrInputProcessed += ptr;
reader.mCurrInputRowStart -= ptr;
// Existing data to move?
if (currAmount > 0) {
System.arraycopy(mBuffer, ptr, mBuffer, 0, currAmount);
minAmount -= currAmount;
}
reader.mInputBuffer = mBuffer;
reader.mInputPtr = 0;
mInputLast = currAmount;
while (minAmount > 0) {
int amount = mBuffer.length - currAmount;
int actual = mReader.read(mBuffer, currAmount, amount);
if (actual < 1) {
if (actual == 0) { // sanity check:
throw new WstxException("Reader (of type "+mReader.getClass().getName()+") returned 0 characters, even when asked to read up to "+amount, getLocation());
}
reader.mInputEnd = mInputLast = currAmount;
return false;
}
currAmount += actual;
minAmount -= actual;
}
reader.mInputEnd = mInputLast = currAmount;
return true;
}
@Override
public void close() throws IOException
{
/* Buffer gets nullified by call to close() or closeCompletely(),
* no need to call second time
*/
if (mBuffer != null) { // so that it's ok to call multiple times
closeAndRecycle(mDoRealClose);
}
}
@Override
public void closeCompletely() throws IOException
{
/* Only need to call if the Reader is not yet null... since
* buffer may have been cleaned by a call to close()
*/
if (mReader != null) { // so that it's ok to call multiple times
closeAndRecycle(true);
}
}
private void closeAndRecycle(boolean fullClose)
throws IOException
{
char[] buf = mBuffer;
// Can we recycle buffers?
if (buf != null) {
mBuffer = null;
mConfig.freeFullCBuffer(buf);
}
// How about Reader; close and/or recycle its buffers?
if (mReader != null) {
if (mReader instanceof BaseReader) {
((BaseReader) mReader).freeBuffers();
}
if (fullClose) {
Reader r = mReader;
mReader = null;
r.close();
}
}
}
}