blob: 4496942619157a60bb74e8547259ff2ba11ccf10 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: ReaderMgr.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/BinMemInputStream.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/UnexpectedEOFException.hpp>
#include <xercesc/util/XMLURL.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/XMLUri.hpp>
#include <xercesc/sax/InputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <xercesc/framework/URLInputSource.hpp>
#include <xercesc/framework/XMLBuffer.hpp>
#include <xercesc/framework/XMLDocumentHandler.hpp>
#include <xercesc/framework/XMLEntityDecl.hpp>
#include <xercesc/framework/XMLEntityHandler.hpp>
#include <xercesc/internal/EndOfEntityException.hpp>
#include <xercesc/internal/ReaderMgr.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/util/XMLResourceIdentifier.hpp>
XERCES_CPP_NAMESPACE_BEGIN
// ---------------------------------------------------------------------------
// ReaderMgr: Constructors and Destructor
// ---------------------------------------------------------------------------
ReaderMgr::ReaderMgr(MemoryManager* const manager) :
fCurEntity(0)
, fCurReader(0)
, fEntityHandler(0)
, fEntityStack(0)
, fNextReaderNum(1)
, fReaderStack(0)
, fThrowEOE(false)
, fXMLVersion(XMLReader::XMLV1_0)
, fStandardUriConformant(false)
, fMemoryManager(manager)
{
}
ReaderMgr::~ReaderMgr()
{
//
// Clean up the reader and entity stacks. Note that we don't own the
// entities, so we don't delete the current entity (and the entity stack
// does not own its elements either, so deleting it will not delete the
// entities it still references!)
//
delete fCurReader;
delete fReaderStack;
delete fEntityStack;
}
// ---------------------------------------------------------------------------
// ReaderMgr: Getter methods
// ---------------------------------------------------------------------------
bool ReaderMgr::isEmpty() const
{
return fReaderStack->empty();
}
// ---------------------------------------------------------------------------
// ReaderMgr: Scanning APIs
// ---------------------------------------------------------------------------
XMLCh ReaderMgr::getNextChar()
{
XMLCh chRet;
if (fCurReader->getNextChar(chRet))
return chRet;
//
// Didn't get anything back so this reader is hosed. So lets move to
// the next reader on the stack. If this fails, it will be because
// its the end of the original file, and we just return zero.
//
// If its the end of an entity and fThrowEOE is set, it will throw out
// of here. Otherwise, it will take us down to the next reader and
// we'll have more chars.
//
if (!popReader())
return XMLCh(0);
// Else try again and return the new character
fCurReader->getNextChar(chRet);
return chRet;
}
void ReaderMgr::getSpaces(XMLBuffer& toFill)
{
// Reset the buffer before we start
toFill.reset();
while (true)
{
//
// Get all the spaces from the current reader. If it returns true,
// it hit a non-space and we are done. Else we have to pop a reader
// and keep going.
//
if (fCurReader->getSpaces(toFill))
break;
// We wore that one out, so lets pop a reader and try again
if (!popReader())
break;
}
}
void ReaderMgr::getUpToCharOrWS(XMLBuffer& toFill, const XMLCh toCheck)
{
// Reset the target buffer before we start
toFill.reset();
//
// Ok, enter a loop where we ask the current reader to get chars until
// it meets the criteria. It returns false if it came back due to eating
// up all of its data. Else it returned because something matched, and
// we are done.
//
while (true)
{
if (fCurReader->getUpToCharOrWS(toFill, toCheck))
break;
// We ate that one up, lets try to pop another. If not, break out
if (!popReader())
break;
}
}
XMLCh ReaderMgr::peekNextChar()
{
XMLCh chRet;
if (fCurReader->peekNextChar(chRet))
return chRet;
//
// Didn't get anything back so this reader is hosed. So lets move to
// the next reader on the stack. If this fails, it will be because
// its the end of the original file, and we just return zero.
//
if (!popReader())
return XMLCh(0);
// Else peek again and return the character
fCurReader->peekNextChar(chRet);
return chRet;
}
bool ReaderMgr::skippedChar(const XMLCh toCheck)
{
while (true)
{
// If we get it, then just return true now
if (fCurReader->skippedChar(toCheck))
return true;
//
// Check to see if we hit end of input on this reader. If so, then
// lets pop and try again. Else, we failed. If we cannot pop another
// then we failed.
//
if (!fCurReader->getNoMoreFlag())
break;
if (!popReader())
break;
}
return false;
}
bool ReaderMgr::skippedSpace()
{
while (true)
{
// If we get it, then just return true now
if (fCurReader->skippedSpace())
return true;
//
// Check to see if we hit end of input on this reader. If so, then
// lets pop and try again. Else, we failed. If we cannot pop another
// then we failed.
//
if (!fCurReader->getNoMoreFlag())
break;
if (!popReader())
break;
}
return false;
}
bool ReaderMgr::skipIfQuote(XMLCh& chGotten)
{
while (true)
{
// If we get it, then just return true now
if (fCurReader->skipIfQuote(chGotten))
return true;
//
// Check to see if we hit end of input on this reader. If so, then
// lets pop and try again. Else, we failed. If we cannot pop another
// then we failed.
//
if (!fCurReader->getNoMoreFlag())
break;
if (!popReader())
break;
}
return false;
}
bool ReaderMgr::skipPastSpaces(bool inDecl)
{
bool skippedSomething = false;
bool tmpFlag;
while (true)
{
//
// Skip all the spaces in the current reader. If it returned because
// it hit a non-space, break out. Else we have to pop another entity
// and keep going.
//
if (fCurReader->skipSpaces(tmpFlag, inDecl))
break;
if (tmpFlag)
skippedSomething = true;
// Try to pop another enitity. If we can't then we are done
if (!popReader())
break;
}
return (tmpFlag || skippedSomething);
}
void ReaderMgr::skipQuotedString(const XMLCh quoteCh)
{
XMLCh nextCh;
while (true)
{
nextCh = getNextChar();
// If we get an end of file char, then return
if (!nextCh)
break;
// If we get the quote char, then break out
if (nextCh == quoteCh)
break;
}
}
XMLCh ReaderMgr::skipUntilIn(const XMLCh* const listToSkip)
{
XMLCh nextCh;
while (true)
{
nextCh = peekNextChar();
if (!nextCh)
break;
if (XMLString::indexOf(listToSkip, nextCh) != -1)
break;
// Its one of ours so eat it
getNextChar();
}
return nextCh;
}
XMLCh ReaderMgr::skipUntilInOrWS(const XMLCh* const listToSkip)
{
XMLCh nextCh;
while (true)
{
nextCh = peekNextChar();
if (!nextCh)
break;
if (fCurReader->isWhitespace(nextCh))
break;
if (XMLString::indexOf(listToSkip, nextCh) != -1)
break;
// Its one of ours, so eat it
getNextChar();
}
return nextCh;
}
// ---------------------------------------------------------------------------
// ReaderMgr: Control methods
// ---------------------------------------------------------------------------
//
// If the reader stack is empty, then there is only the original main XML
// entity left. If its empty, then we have no more input.
//
bool ReaderMgr::atEOF() const
{
return fReaderStack->empty() && fCurReader->getNoMoreFlag();
}
//
// This method is called in the case of errors to clean up the stack when
// entities have been incorrectly left on the stack due to syntax errors.
// It just cleans back the stack, and sends no entity events.
//
void ReaderMgr::cleanStackBackTo(const unsigned int readerNum)
{
//
// Just start popping readers until we find the one with the indicated
// reader number.
//
while (true)
{
if (fCurReader->getReaderNum() == readerNum)
break;
if (fReaderStack->empty())
ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::RdrMgr_ReaderIdNotFound, fMemoryManager);
delete fCurReader;
fCurReader = fReaderStack->pop();
fCurEntity = fEntityStack->pop();
}
}
XMLReader* ReaderMgr::createReader( const InputSource& src
, const bool
, const XMLReader::RefFrom refFrom
, const XMLReader::Types type
, const XMLReader::Sources source
, const bool calcSrcOfs)
{
//
// Ask the input source to create us an input stream. The particular
// type of input source will know what kind to create.
//
BinInputStream* newStream = src.makeStream();
if (!newStream)
return 0;
Janitor<BinInputStream> streamJanitor(newStream);
//
// Create a new reader and return it. If the source has an encoding that
// it wants to force, then we call the constructor that does that.
// Otherwise, we just call the one that provides the provisional encoding
// to be possibly updated later by the encoding="" setting.
//
XMLReader* retVal = 0;
// XMLReader ctor invokes refreshRawBuffer() which calls
// newStream->readBytes().
// This readBytes() may throw exception, which neither
// refresRawBuffer(), nor XMLReader ctor catches.
// We need to handle this exception to avoid leak on newStream.
try {
if (src.getEncoding())
{
retVal = new (fMemoryManager) XMLReader
(
src.getPublicId()
, src.getSystemId()
, newStream
, src.getEncoding()
, refFrom
, type
, source
, false
, calcSrcOfs
, fXMLVersion
, fMemoryManager
);
}
else
{
retVal = new (fMemoryManager) XMLReader
(
src.getPublicId()
, src.getSystemId()
, newStream
, refFrom
, type
, source
, false
, calcSrcOfs
, fXMLVersion
, fMemoryManager
);
}
}
catch(const OutOfMemoryException&)
{
streamJanitor.release();
throw;
}
assert(retVal);
streamJanitor.release();
// Set the next available reader number on this reader
retVal->setReaderNum(fNextReaderNum++);
return retVal;
}
XMLReader* ReaderMgr::createReader( const XMLCh* const sysId
, const XMLCh* const pubId
, const bool xmlDecl
, const XMLReader::RefFrom refFrom
, const XMLReader::Types type
, const XMLReader::Sources source
, InputSource*& srcToFill
, const bool calcSrcOfs
, const bool disableDefaultEntityResolution)
{
//Normalize sysId
XMLBuffer normalizedSysId(1023, fMemoryManager);
if(sysId)
XMLString::removeChar(sysId, 0xFFFF, normalizedSysId);
const XMLCh* normalizedURI = normalizedSysId.getRawBuffer();
// Create a buffer for expanding the system id
XMLBuffer expSysId(1023, fMemoryManager);
//
// Allow the entity handler to expand the system id if they choose
// to do so.
//
if (fEntityHandler)
{
if (!fEntityHandler->expandSystemId(normalizedURI, expSysId))
expSysId.set(normalizedURI);
}
else
{
expSysId.set(normalizedURI);
}
// Call the entity resolver interface to get an input source
srcToFill = 0;
if (fEntityHandler)
{
LastExtEntityInfo lastInfo;
getLastExtEntityInfo(lastInfo);
XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::ExternalEntity,
expSysId.getRawBuffer(), XMLUni::fgZeroLenString, pubId, lastInfo.systemId,
this);
srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier);
}
//
// If they didn't create a source via the entity resolver, then we
// have to create one on our own.
//
if (!srcToFill)
{
if (disableDefaultEntityResolution)
return 0;
LastExtEntityInfo lastInfo;
getLastExtEntityInfo(lastInfo);
// Keep this #if 0 block as it was exposing a threading problem on AIX.
// Got rid of the problem by changing XMLURL to not throw malformedurl
// exceptions.
#if 0
try
{
XMLURL urlTmp(lastInfo.systemId, expSysId.getRawBuffer(), fMemoryManager);
if (urlTmp.isRelative())
{
ThrowXMLwithMemMgr
(
MalformedURLException
, XMLExcepts::URL_NoProtocolPresent
, fMemoryManager
);
}
else {
if (fStandardUriConformant && urlTmp.hasInvalidChar())
ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
}
}
catch(const MalformedURLException& e)
{
// Its not a URL, so lets assume its a local file name if non-standard uri is allowed
if (!fStandardUriConformant)
srcToFill = new (fMemoryManager) LocalFileInputSource
(
lastInfo.systemId
, expSysId.getRawBuffer()
, fMemoryManager
);
else
throw e;
}
#else
XMLURL urlTmp(fMemoryManager);
if ((!urlTmp.setURL(lastInfo.systemId, expSysId.getRawBuffer(), urlTmp)) ||
(urlTmp.isRelative()))
{
if (!fStandardUriConformant)
{
XMLBuffer resolvedSysId(1023, fMemoryManager);
XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId);
srcToFill = new (fMemoryManager) LocalFileInputSource
(
lastInfo.systemId
, resolvedSysId.getRawBuffer()
, fMemoryManager
);
}
else
ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
}
else
{
if (fStandardUriConformant && urlTmp.hasInvalidChar())
ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
}
#endif
}
// Put a janitor on the input source
Janitor<InputSource> janSrc(srcToFill);
//
// Now call the other version with the input source that we have, and
// return the resulting reader.
//
XMLReader* retVal = createReader
(
*srcToFill
, xmlDecl
, refFrom
, type
, source
, calcSrcOfs
);
// Either way, we can release the input source now
janSrc.orphan();
// If it failed for any reason, then return zero.
if (!retVal)
return 0;
// Give this reader the next available reader number and return it
retVal->setReaderNum(fNextReaderNum++);
return retVal;
}
XMLReader* ReaderMgr::createReader( const XMLCh* const baseURI
, const XMLCh* const sysId
, const XMLCh* const pubId
, const bool xmlDecl
, const XMLReader::RefFrom refFrom
, const XMLReader::Types type
, const XMLReader::Sources source
, InputSource*& srcToFill
, const bool calcSrcOfs
, const bool disableDefaultEntityResolution)
{
//Normalize sysId
XMLBuffer normalizedSysId(1023, fMemoryManager);
if(sysId)
XMLString::removeChar(sysId, 0xFFFF, normalizedSysId);
const XMLCh* normalizedURI = normalizedSysId.getRawBuffer();
// Create a buffer for expanding the system id
XMLBuffer expSysId(1023, fMemoryManager);
//
// Allow the entity handler to expand the system id if they choose
// to do so.
//
if (fEntityHandler)
{
if (!fEntityHandler->expandSystemId(normalizedURI, expSysId))
expSysId.set(normalizedURI);
}
else
{
expSysId.set(normalizedURI);
}
// Call the entity resolver interface to get an input source
srcToFill = 0;
if (fEntityHandler)
{
XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::ExternalEntity,
expSysId.getRawBuffer(), XMLUni::fgZeroLenString, pubId, baseURI,
this);
srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier);
}
//
// If they didn't create a source via the entity resolver, then we
// have to create one on our own.
//
if (!srcToFill)
{
if (disableDefaultEntityResolution)
return 0;
LastExtEntityInfo lastInfo;
const XMLCh* baseuri=baseURI;
if(!baseuri || !*baseuri)
{
getLastExtEntityInfo(lastInfo);
baseuri = lastInfo.systemId;
}
XMLURL urlTmp(fMemoryManager);
if ((!urlTmp.setURL(baseuri, expSysId.getRawBuffer(), urlTmp)) ||
(urlTmp.isRelative()))
{
if (!fStandardUriConformant)
{
XMLBuffer resolvedSysId(1023, fMemoryManager);
XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId);
srcToFill = new (fMemoryManager) LocalFileInputSource
(
baseuri
, resolvedSysId.getRawBuffer()
, fMemoryManager
);
}
else
ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
}
else
{
if (fStandardUriConformant && urlTmp.hasInvalidChar())
ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager);
srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager);
}
}
// Put a janitor on the input source
Janitor<InputSource> janSrc(srcToFill);
//
// Now call the other version with the input source that we have, and
// return the resulting reader.
//
XMLReader* retVal = createReader
(
*srcToFill
, xmlDecl
, refFrom
, type
, source
, calcSrcOfs
);
// Either way, we can release the input source now
janSrc.orphan();
// If it failed for any reason, then return zero.
if (!retVal)
return 0;
// Give this reader the next available reader number and return it
retVal->setReaderNum(fNextReaderNum++);
return retVal;
}
XMLReader*
ReaderMgr::createIntEntReader( const XMLCh* const sysId
, const XMLReader::RefFrom refFrom
, const XMLReader::Types type
, const XMLCh* const dataBuf
, const unsigned int dataLen
, const bool copyBuf
, const bool calcSrcOfs)
{
//
// This one is easy, we just create an input stream for the data and
// provide a few extra goodies.
//
// NOTE: We use a special encoding string that will be recognized
// as a 'do nothing' transcoder for the already internalized XMLCh
// data that makes up an internal entity.
//
BinMemInputStream* newStream = new (fMemoryManager) BinMemInputStream
(
(const XMLByte*)dataBuf
, dataLen * sizeof(XMLCh)
, copyBuf ? BinMemInputStream::BufOpt_Copy
: BinMemInputStream::BufOpt_Reference
, fMemoryManager
);
if (!newStream)
return 0;
XMLReader* retVal = new (fMemoryManager) XMLReader
(
sysId
, 0
, newStream
, XMLRecognizer::XERCES_XMLCH
, refFrom
, type
, XMLReader::Source_Internal
, false
, calcSrcOfs
, fXMLVersion
, fMemoryManager
);
// If it failed for any reason, then return zero.
if (!retVal) {
delete newStream;
return 0;
}
// Set the reader number to the next available number
retVal->setReaderNum(fNextReaderNum++);
return retVal;
}
const XMLCh* ReaderMgr::getCurrentEncodingStr() const
{
const XMLEntityDecl* theEntity;
const XMLReader* theReader = getLastExtEntity(theEntity);
return theReader->getEncodingStr();
}
const XMLEntityDecl* ReaderMgr::getCurrentEntity() const
{
return fCurEntity;
}
XMLEntityDecl* ReaderMgr::getCurrentEntity()
{
return fCurEntity;
}
unsigned int ReaderMgr::getReaderDepth() const
{
// If the stack doesn't exist, its obviously zero
if (!fEntityStack)
return 0;
//
// The return is the stack size, plus one if there is a current
// reader. So if there is no current reader and none on the stack,
// its zero, else its some non-zero value.
//
unsigned int retVal = fEntityStack->size();
if (fCurReader)
retVal++;
return retVal;
}
void ReaderMgr::getLastExtEntityInfo(LastExtEntityInfo& lastInfo) const
{
//
// If the reader stack never got created or we've not managed to open any
// main entity yet, then we can't give this information.
//
if (!fReaderStack || !fCurReader)
{
lastInfo.systemId = XMLUni::fgZeroLenString;
lastInfo.publicId = XMLUni::fgZeroLenString;
lastInfo.lineNumber = 0;
lastInfo.colNumber = 0;
return;
}
// We have at least one entity so get the data
const XMLEntityDecl* theEntity;
const XMLReader* theReader = getLastExtEntity(theEntity);
// Fill in the info structure with the reader we found
lastInfo.systemId = theReader->getSystemId();
lastInfo.publicId = theReader->getPublicId();
lastInfo.lineNumber = theReader->getLineNumber();
lastInfo.colNumber = theReader->getColumnNumber();
}
bool ReaderMgr::isScanningPERefOutOfLiteral() const
{
// If the current reader is not for an entity, then definitely not
if (!fCurEntity)
return false;
//
// If this is a PE entity, and its not being expanded in a literal
// then its true.
//
if ((fCurReader->getType() == XMLReader::Type_PE)
&& (fCurReader->getRefFrom() == XMLReader::RefFrom_NonLiteral))
{
return true;
}
return false;
}
bool ReaderMgr::pushReader( XMLReader* const reader
, XMLEntityDecl* const entity)
{
//
// First, if an entity was passed, we have to confirm that this entity
// is not already on the entity stack. If so, then this is a recursive
// entity expansion, so we issue an error and refuse to put the reader
// on the stack.
//
// If there is no entity passed, then its not an entity being pushed, so
// nothing to do. If there is no entity stack yet, then of coures it
// cannot already be there.
//
if (entity && fEntityStack)
{
const unsigned int count = fEntityStack->size();
const XMLCh* const theName = entity->getName();
for (unsigned int index = 0; index < count; index++)
{
const XMLEntityDecl* curDecl = fEntityStack->elementAt(index);
if (curDecl)
{
if (XMLString::equals(theName, curDecl->getName()))
{
// Oops, already there so delete reader and return
delete reader;
return false;
}
}
}
}
//
// Fault in the reader stack. Give it an initial capacity of 16, and
// tell it it does own its elements.
//
if (!fReaderStack)
fReaderStack = new (fMemoryManager) RefStackOf<XMLReader>(16, true, fMemoryManager);
// And the entity stack, which does not own its elements
if (!fEntityStack)
fEntityStack = new (fMemoryManager) RefStackOf<XMLEntityDecl>(16, false, fMemoryManager);
//
// Push the current reader and entity onto their respective stacks.
// Note that the the current entity can be null if the current reader
// is not for an entity.
//
if (fCurReader)
{
fReaderStack->push(fCurReader);
fEntityStack->push(fCurEntity);
}
//
// Make the passed reader and entity the current top of stack. The
// passed entity can (and often is) null.
//
fCurReader = reader;
fCurEntity = entity;
return true;
}
void ReaderMgr::reset()
{
// Reset all of the flags
fThrowEOE = false;
// Delete the current reader and flush the reader stack
delete fCurReader;
fCurReader = 0;
if (fReaderStack)
fReaderStack->removeAllElements();
//
// And do the same for the entity stack, but don't delete the current
// entity (if any) since we don't own them.
//
fCurEntity = 0;
if (fEntityStack)
fEntityStack->removeAllElements();
}
// ---------------------------------------------------------------------------
// ReaderMgr: Implement the SAX Locator interface
// ---------------------------------------------------------------------------
const XMLCh* ReaderMgr::getPublicId() const
{
if (!fReaderStack && !fCurReader)
return XMLUni::fgZeroLenString;
const XMLEntityDecl* theEntity;
return getLastExtEntity(theEntity)->getPublicId();
}
const XMLCh* ReaderMgr::getSystemId() const
{
if (!fReaderStack && !fCurReader)
return XMLUni::fgZeroLenString;
const XMLEntityDecl* theEntity;
return getLastExtEntity(theEntity)->getSystemId();
}
XMLSSize_t ReaderMgr::getColumnNumber() const
{
if (!fReaderStack && !fCurReader)
return 0;
const XMLEntityDecl* theEntity;
return getLastExtEntity(theEntity)->getColumnNumber();
}
XMLSSize_t ReaderMgr::getLineNumber() const
{
if (!fReaderStack && !fCurReader)
return 0;
const XMLEntityDecl* theEntity;
return getLastExtEntity(theEntity)->getLineNumber();
}
// ---------------------------------------------------------------------------
// ReaderMgr: Private helper methods
// ---------------------------------------------------------------------------
const XMLReader*
ReaderMgr::getLastExtEntity(const XMLEntityDecl*& itsEntity) const
{
//
// Scan down the reader stack until we find a reader for an entity that
// is external. First check that there is anything in the stack at all,
// in which case the current reader is the main file and that's the one
// that we want.
//
const XMLReader* theReader = fCurReader;
//
// If there is a current entity and it is not an external entity, then
// search the stack; else, keep the reader that we've got since its
// either an external entity reader or the main file reader.
//
const XMLEntityDecl* curEntity = fCurEntity;
if (curEntity && !curEntity->isExternal())
{
unsigned int index = fReaderStack->size();
if (index)
{
while (true)
{
// Move down to the previous element and get a pointer to it
index--;
curEntity = fEntityStack->elementAt(index);
//
// If its null or its an external entity, then this reader
// is what we want, so break out with that one.
//
if (!curEntity)
{
theReader = fReaderStack->elementAt(index);
break;
}
else if (curEntity->isExternal())
{
theReader = fReaderStack->elementAt(index);
break;
}
// We hit the end, so leave the main file reader as the one
if (!index)
break;
}
}
}
itsEntity = curEntity;
return theReader;
}
bool ReaderMgr::popReader()
{
//
// We didn't get any more, so try to pop off a reader. If the reader
// stack is empty, then we are at the end, so return false.
//
if (fReaderStack->empty())
return false;
//
// Remember the current entity, before we pop off a new one. We might
// need this to throw the end of entity exception at the end.
//
XMLEntityDecl* prevEntity = fCurEntity;
const bool prevReaderThrowAtEnd = fCurReader->getThrowAtEnd();
const unsigned int readerNum = fCurReader->getReaderNum();
//
// Delete the current reader and pop a new reader and entity off
// the stacks.
//
delete fCurReader;
fCurReader = fReaderStack->pop();
fCurEntity = fEntityStack->pop();
//
// If there was a previous entity, and either the fThrowEOE flag is set
// or reader was marked as such, then throw an end of entity.
//
if (prevEntity && fThrowEOE || prevReaderThrowAtEnd)
throw EndOfEntityException(prevEntity, readerNum);
while (true)
{
//
// They don't want us to throw, so lets just return with a new
// reader. Here we have to do a loop because we might have multiple
// readers on these stack that are empty (i.e. the last char in them
// was the ';' at the end of the entity ref that caused the next
// entity to be pushed.
//
// So we loop until we find a non-empty reader, or hit the main
// file entity. If we find one with some chars available, then break
// out and take that one.
//
if (fCurReader->charsLeftInBuffer())
break;
fCurReader->refreshCharBuffer();
if (fCurReader->charsLeftInBuffer())
break;
//
// The current one is hosed. So, if the reader stack is empty we
// are dead meat and can give up now.
//
if (fReaderStack->empty())
return false;
// Else pop again and try it one more time
delete fCurReader;
fCurReader = fReaderStack->pop();
fCurEntity = fEntityStack->pop();
}
return true;
}
XERCES_CPP_NAMESPACE_END