diff options
Diffstat (limited to 'libxerces-c/xercesc/internal/ReaderMgr.cpp')
-rw-r--r--[l---------] | libxerces-c/xercesc/internal/ReaderMgr.cpp | 1124 |
1 files changed, 1123 insertions, 1 deletions
diff --git a/libxerces-c/xercesc/internal/ReaderMgr.cpp b/libxerces-c/xercesc/internal/ReaderMgr.cpp index 6604eb1..d14483e 120000..100644 --- a/libxerces-c/xercesc/internal/ReaderMgr.cpp +++ b/libxerces-c/xercesc/internal/ReaderMgr.cpp @@ -1 +1,1123 @@ -../../../upstream/src/xercesc/internal/ReaderMgr.cpp
\ No newline at end of file +/* + * 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$ + */ + +// --------------------------------------------------------------------------- +// 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(); + + // + // 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. + // + while (!fCurReader->getSpaces(toFill)) + { + // 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 (!fCurReader->getUpToCharOrWS(toFill, toCheck)) + { + // 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; +} + +void ReaderMgr::skipPastSpaces(bool& skippedSomething, bool inDecl /* = false */) +{ + // we rely on the fact that fCurReader->skipSpaces will NOT reset the flag to false, but only + // set it to true if a space is found + skippedSomething = false; + // + // 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. + // + while (!fCurReader->skipSpaces(skippedSomething, inDecl)) + { + // Try to pop another entity. If we can't then we are done + if (!popReader()) + break; + } +} + +void ReaderMgr::skipPastSpaces() +{ + // we are not using it, so we don't care to initialize it + bool tmpFlag; + // + // 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. + // + while (!fCurReader->skipSpaces(tmpFlag, false)) + { + // Try to pop another entity. If we can't then we are done + if (!popReader()) + break; + } +} + +void ReaderMgr::skipQuotedString(const XMLCh quoteCh) +{ + XMLCh nextCh; + // If we get an end of file char, then return + while ((nextCh = getNextChar())!=0) + { + // If we get the quote char, then break out + if (nextCh == quoteCh) + break; + } +} + + +XMLCh ReaderMgr::skipUntilIn(const XMLCh* const listToSkip) +{ + XMLCh nextCh; + // If we get an end of file char, then return + while ((nextCh = peekNextChar())!=0) + { + 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; + // If we get an end of file char, then return + while ((nextCh = peekNextChar())!=0) + { + 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 XMLSize_t 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 + , XMLSize_t lowWaterMark) +{ + // + // 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 { + const XMLCh* encoding = src.getEncoding(); + if(encoding == 0) + encoding = newStream->getEncoding(); + if (encoding) + { + retVal = new (fMemoryManager) XMLReader + ( + src.getPublicId() + , src.getSystemId() + , newStream + , encoding + , refFrom + , type + , source + , false + , calcSrcOfs + , lowWaterMark + , fXMLVersion + , fMemoryManager + ); + } + else + { + retVal = new (fMemoryManager) XMLReader + ( + src.getPublicId() + , src.getSystemId() + , newStream + , refFrom + , type + , source + , false + , calcSrcOfs + , lowWaterMark + , 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 + , XMLSize_t lowWaterMark + , 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 + , lowWaterMark + ); + + // 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 + , XMLSize_t lowWaterMark + , const bool disableDefaultEntityResolution) +{ + //Normalize sysId + XMLBuffer normalizedSysId(1023, fMemoryManager); + 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 + , lowWaterMark + ); + + // 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 XMLSize_t dataLen + , const bool copyBuf + , const bool calcSrcOfs + , XMLSize_t lowWaterMark) +{ + // + // 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 + , lowWaterMark + , 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; +} + + +XMLSize_t 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. + // + XMLSize_t 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 XMLSize_t count = fEntityStack->size(); + const XMLCh* const theName = entity->getName(); + for (XMLSize_t 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(); +} + +XMLFileLoc ReaderMgr::getColumnNumber() const +{ + if (!fReaderStack && !fCurReader) + return 0; + + const XMLEntityDecl* theEntity; + return getLastExtEntity(theEntity)->getColumnNumber(); +} + +XMLFileLoc 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()) + { + XMLSize_t 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 XMLSize_t 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 |