Skip to content
Snippets Groups Projects
IGXMLScanner.cpp 116 KiB
Newer Older
Khaled Noaman's avatar
Khaled Noaman committed
/*
 * 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.
Khaled Noaman's avatar
Khaled Noaman committed
 */

/*
 * $Id$
Khaled Noaman's avatar
Khaled Noaman committed
 */


// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/internal/IGXMLScanner.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/HashPtr.hpp>
Khaled Noaman's avatar
Khaled Noaman committed
#include <xercesc/util/UnexpectedEOFException.hpp>
#include <xercesc/sax/InputSource.hpp>
#include <xercesc/framework/XMLDocumentHandler.hpp>
#include <xercesc/framework/XMLEntityHandler.hpp>
#include <xercesc/framework/XMLPScanToken.hpp>
#include <xercesc/internal/EndOfEntityException.hpp>
#include <xercesc/framework/MemoryManager.hpp>
#include <xercesc/framework/XMLGrammarPool.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed
#include <xercesc/framework/XMLDTDDescription.hpp>
#include <xercesc/framework/psvi/PSVIElement.hpp>
#include <xercesc/framework/psvi/PSVIHandler.hpp>
#include <xercesc/framework/psvi/PSVIAttributeList.hpp>
#include <xercesc/validators/common/GrammarResolver.hpp>
Khaled Noaman's avatar
Khaled Noaman committed
#include <xercesc/validators/DTD/DocTypeHandler.hpp>
#include <xercesc/validators/DTD/DTDScanner.hpp>
#include <xercesc/validators/DTD/DTDValidator.hpp>
#include <xercesc/validators/schema/SchemaValidator.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed
#include <xercesc/validators/schema/identity/IdentityConstraintHandler.hpp>
Khaled Noaman's avatar
Khaled Noaman committed
#include <xercesc/validators/schema/identity/IC_Selector.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
Khaled Noaman's avatar
Khaled Noaman committed

XERCES_CPP_NAMESPACE_BEGIN


typedef JanitorMemFunCall<IGXMLScanner> CleanupType;
typedef JanitorMemFunCall<ReaderMgr>    ReaderMgrResetType;


Khaled Noaman's avatar
Khaled Noaman committed
// ---------------------------------------------------------------------------
//  IGXMLScanner: Constructors and Destructor
// ---------------------------------------------------------------------------
IGXMLScanner::IGXMLScanner( XMLValidator* const  valToAdopt
                          , GrammarResolver* const grammarResolver
                          , MemoryManager* const manager) :
Khaled Noaman's avatar
Khaled Noaman committed

    XMLScanner(valToAdopt, grammarResolver, manager)
Khaled Noaman's avatar
Khaled Noaman committed
    , fSeeXsi(false)
Khaled Noaman's avatar
Khaled Noaman committed
    , fElemStateSize(16)
    , fElemState(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fRawAttrList(0)
    , fRawAttrColonListSize(32)
    , fRawAttrColonList(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fDTDValidator(0)
    , fSchemaValidator(0)
    , fDTDGrammar(0)
PeiYong Zhang's avatar
PeiYong Zhang committed
    , fICHandler(0)
    , fDTDElemNonDeclPool(0)
    , fSchemaElemNonDeclPool(0)
    , fElemCount(0)
    , fAttDefRegistry(0)
    , fUndeclaredAttrRegistry(0)
    , fUndeclaredAttrRegistryNS(0)
    , fPSVIAttrList(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fModel(0)
    , fPSVIElement(0)
Khaled Noaman's avatar
Khaled Noaman committed
{
    CleanupType cleanup(this, &IGXMLScanner::cleanUp);

Khaled Noaman's avatar
Khaled Noaman committed
    try
    {
        commonInit();
Khaled Noaman's avatar
Khaled Noaman committed
    }
        // Don't cleanup when out of memory, since executing the
        // code can cause problems.
        cleanup.release();


    cleanup.release();
Khaled Noaman's avatar
Khaled Noaman committed
}

IGXMLScanner::IGXMLScanner( XMLDocumentHandler* const docHandler
                          , DocTypeHandler* const     docTypeHandler
                          , XMLEntityHandler* const   entityHandler
                          , XMLErrorReporter* const   errHandler
                          , XMLValidator* const       valToAdopt
                          , GrammarResolver* const    grammarResolver
                          , MemoryManager* const      manager) :
Khaled Noaman's avatar
Khaled Noaman committed

    XMLScanner(docHandler, docTypeHandler, entityHandler, errHandler, valToAdopt, grammarResolver, manager)
Khaled Noaman's avatar
Khaled Noaman committed
    , fSeeXsi(false)
Khaled Noaman's avatar
Khaled Noaman committed
    , fElemStateSize(16)
    , fElemState(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fRawAttrList(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fDTDValidator(0)
    , fSchemaValidator(0)
    , fDTDGrammar(0)
PeiYong Zhang's avatar
PeiYong Zhang committed
    , fICHandler(0)
    , fDTDElemNonDeclPool(0)
    , fSchemaElemNonDeclPool(0)
    , fElemCount(0)
    , fAttDefRegistry(0)
    , fUndeclaredAttrRegistry(0)
    , fUndeclaredAttrRegistryNS(0)
    , fPSVIAttrList(0)
Khaled Noaman's avatar
Khaled Noaman committed
    , fModel(0)
    , fPSVIElement(0)
Khaled Noaman's avatar
Khaled Noaman committed
{
    CleanupType cleanup(this, &IGXMLScanner::cleanUp);

Khaled Noaman's avatar
Khaled Noaman committed
    try
    {	
        commonInit();
    }
        // Don't cleanup when out of memory, since executing the
        // code can cause problems.
        cleanup.release();


    cleanup.release();
Khaled Noaman's avatar
Khaled Noaman committed
}

IGXMLScanner::~IGXMLScanner()
{
    cleanUp();
}

// ---------------------------------------------------------------------------
//  XMLScanner: Getter methods
// ---------------------------------------------------------------------------
NameIdPool<DTDEntityDecl>* IGXMLScanner::getEntityDeclPool()
{
Khaled Noaman's avatar
Khaled Noaman committed
    return fDTDGrammar->getEntityDeclPool();
}

const NameIdPool<DTDEntityDecl>* IGXMLScanner::getEntityDeclPool() const
{
Khaled Noaman's avatar
Khaled Noaman committed
    return fDTDGrammar->getEntityDeclPool();
}

// ---------------------------------------------------------------------------
//  IGXMLScanner: Main entry point to scan a document
// ---------------------------------------------------------------------------
void IGXMLScanner::scanDocument(const InputSource& src)
{
    //  Bump up the sequence id for this parser instance. This will invalidate
    //  any previous progressive scan tokens.
    fSequenceId++;

    ReaderMgrResetType  resetReaderMgr(&fReaderMgr, &ReaderMgr::reset);

Khaled Noaman's avatar
Khaled Noaman committed
    try
    {
        //  Reset the scanner and its plugged in stuff for a new run. This
        //  resets all the data structures, creates the initial reader and
        //  pushes it on the stack, and sets up the base document path.
        scanReset(src);

        // If we have a document handler, then call the start document
        if (fDocHandler)
            fDocHandler->startDocument();

        //  Scan the prolog part, which is everything before the root element
        //  including the DTD subsets.
        scanProlog();

        //  If we got to the end of input, then its not a valid XML file.
        //  Else, go on to scan the content.
        if (fReaderMgr.atEOF())
        {
            emitError(XMLErrs::EmptyMainEntity);
        }
        else
        {
            // Scan content, and tell it its not an external entity
Khaled Noaman's avatar
Khaled Noaman committed
            {
                // Do post-parse validation if required
                if (fValidate)
                {
                    //  We handle ID reference semantics at this level since
                    //  its required by XML 1.0.
                    checkIDRefs();

                    // Then allow the validator to do any extra stuff it wants
//                    fValidator->postParseValidation();
                }

                // That went ok, so scan for any miscellaneous stuff
                if (!fReaderMgr.atEOF())
                    scanMiscellaneous();
            }
        }

        // If we have a document handler, then call the end document
        if (fDocHandler)
            fDocHandler->endDocument();

Khaled Noaman's avatar
Khaled Noaman committed
    }
    //  NOTE:
    //
    //  In all of the error processing below, the emitError() call MUST come
    //  before the flush of the reader mgr, or it will fail because it tries
    //  to find out the position in the XML source of the error.
    catch(const XMLErrs::Codes)
    {
        // This is a 'first failure' exception, so fall through
Khaled Noaman's avatar
Khaled Noaman committed
    }
    catch(const XMLValid::Codes)
    {
        // This is a 'first fatal error' type exit, so fall through
Khaled Noaman's avatar
Khaled Noaman committed
    }
    catch(const XMLException& excToCatch)
    {
        //  Emit the error and catch any user exception thrown from here. Make
        //  sure in all cases we flush the reader manager.
        fInException = true;
        try
        {
            if (excToCatch.getErrorType() == XMLErrorReporter::ErrType_Warning)
                emitError
                (
                    XMLErrs::XMLException_Warning
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
            else if (excToCatch.getErrorType() >= XMLErrorReporter::ErrType_Fatal)
                emitError
                (
                    XMLErrs::XMLException_Fatal
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
            else
                emitError
                (
                    XMLErrs::XMLException_Error
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
        }
            // This is a special case for out-of-memory
            // conditions, because resetting the ReaderMgr
            // can be problematic.
            resetReaderMgr.release();

Khaled Noaman's avatar
Khaled Noaman committed
    }
        // This is a special case for out-of-memory
        // conditions, because resetting the ReaderMgr
        // can be problematic.
        resetReaderMgr.release();

Khaled Noaman's avatar
Khaled Noaman committed
        throw;
    }
}


bool IGXMLScanner::scanNext(XMLPScanToken& token)
{
    // Make sure this token is still legal
    if (!isLegalToken(token))
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Scan_BadPScanToken, fMemoryManager);
Khaled Noaman's avatar
Khaled Noaman committed

    // Find the next token and remember the reader id
    XMLSize_t orgReader;
Khaled Noaman's avatar
Khaled Noaman committed
    XMLTokens curToken;

    ReaderMgrResetType  resetReaderMgr(&fReaderMgr, &ReaderMgr::reset);

Khaled Noaman's avatar
Khaled Noaman committed
    bool retVal = true;

    try
    {
        while (true)
        {
            //  We have to handle any end of entity exceptions that happen here.
            //  We could be at the end of X nested entities, each of which will
            //  generate an end of entity exception as we try to move forward.
            try
            {
                curToken = senseNextToken(orgReader);
                break;
            }
            catch(const EndOfEntityException& toCatch)
            {
                // Send an end of entity reference event
                if (fDocHandler)
                    fDocHandler->endEntityReference(toCatch.getEntity());
            }
        }

        if (curToken == Token_CharData)
        {
            scanCharData(fCDataBuf);
        }
        else if (curToken == Token_EOF)
        {
            if (!fElemStack.isEmpty())
            {
                const ElemStack::StackElem* topElem = fElemStack.popTop();
                emitError
                (
                    XMLErrs::EndedWithTagsOnStack
                    , topElem->fThisElement->getFullName()
                );
            }

            retVal = false;
        }
        else
        {
            // Its some sort of markup
            bool gotData = true;
            switch(curToken)
            {
                case Token_CData :
                    // Make sure we are within content
                    if (fElemStack.isEmpty())
                        emitError(XMLErrs::CDATAOutsideOfContent);
                    scanCDSection();
                    break;

                case Token_Comment :
                    scanComment();
                    break;

                case Token_EndTag :
                    scanEndTag(gotData);
                    break;

                case Token_PI :
                    scanPI();
                    break;

                case Token_StartTag :
                    if (fDoNamespaces)
                        scanStartTagNS(gotData);
                    else
                        scanStartTag(gotData);
                    break;

                default :
                    fReaderMgr.skipToChar(chOpenAngle);
                    break;
            }

            if (orgReader != fReaderMgr.getCurrentReaderNum())
                emitError(XMLErrs::PartialMarkupInEntity);

            // If we hit the end, then do the miscellaneous part
            if (!gotData)
            {
                // Do post-parse validation if required
                if (fValidate)
                {
                    //  We handle ID reference semantics at this level since
                    //  its required by XML 1.0.
                    checkIDRefs();

                    // Then allow the validator to do any extra stuff it wants
//                    fValidator->postParseValidation();
                }

                // That went ok, so scan for any miscellaneous stuff
                scanMiscellaneous();

PeiYong Zhang's avatar
PeiYong Zhang committed
                if (toCheckIdentityConstraint())
                    fICHandler->endDocument();
Khaled Noaman's avatar
Khaled Noaman committed

                if (fDocHandler)
                    fDocHandler->endDocument();
            }
        }
    }
    //  NOTE:
    //
    //  In all of the error processing below, the emitError() call MUST come
    //  before the flush of the reader mgr, or it will fail because it tries
    //  to find out the position in the XML source of the error.
    catch(const XMLErrs::Codes)
    {
        // This is a 'first failure' exception so return failure
        retVal = false;
Khaled Noaman's avatar
Khaled Noaman committed
    }
    catch(const XMLValid::Codes)
    {
        // This is a 'first fatal error' type exit, so return failure
        retVal = false;
Khaled Noaman's avatar
Khaled Noaman committed
    }
    catch(const XMLException& excToCatch)
    {
        //  Emit the error and catch any user exception thrown from here. Make
        //  sure in all cases we flush the reader manager.
        fInException = true;
        try
        {
            if (excToCatch.getErrorType() == XMLErrorReporter::ErrType_Warning)
                emitError
                (
                    XMLErrs::XMLException_Warning
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
            else if (excToCatch.getErrorType() >= XMLErrorReporter::ErrType_Fatal)
                emitError
                (
                    XMLErrs::XMLException_Fatal
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
            else
                emitError
                (
                    XMLErrs::XMLException_Error
Khaled Noaman's avatar
Khaled Noaman committed
                    , excToCatch.getMessage()
                );
        }
            // This is a special case for out-of-memory
            // conditions, because resetting the ReaderMgr
            // can be problematic.
            resetReaderMgr.release();

Khaled Noaman's avatar
Khaled Noaman committed
            throw;
        }

        retVal = false;
Khaled Noaman's avatar
Khaled Noaman committed
    }
        // This is a special case for out-of-memory
        // conditions, because resetting the ReaderMgr
        // can be problematic.
        resetReaderMgr.release();

Khaled Noaman's avatar
Khaled Noaman committed
        throw;
    }

    // If we are not at the end, release the object that will
    // reset the ReaderMgr.
    if (retVal)
        resetReaderMgr.release();
Khaled Noaman's avatar
Khaled Noaman committed

    return retVal;
}



// ---------------------------------------------------------------------------
//  IGXMLScanner: Private helper methods. Most of these are implemented in
//  IGXMLScanner2.Cpp.
// ---------------------------------------------------------------------------

//  This method handles the common initialization, to avoid having to do
//  it redundantly in multiple constructors.
void IGXMLScanner::commonInit()
{

    //  Create the element state array
    fElemState = (unsigned int*) fMemoryManager->allocate
    (
        fElemStateSize * sizeof(unsigned int)
    ); //new unsigned int[fElemStateSize];
Khaled Noaman's avatar
Khaled Noaman committed

    //  And we need one for the raw attribute scan. This just stores key/
    //  value string pairs (prior to any processing.)
    fRawAttrList = new (fMemoryManager) RefVectorOf<KVStringPair>(32, true, fMemoryManager);
    fRawAttrColonList = (int*) fMemoryManager->allocate
    (
        fRawAttrColonListSize * sizeof(int)
    );
Khaled Noaman's avatar
Khaled Noaman committed

    //  Create the Validator and init them
    fDTDValidator = new (fMemoryManager) DTDValidator();
Khaled Noaman's avatar
Khaled Noaman committed
    initValidator(fDTDValidator);
    fSchemaValidator = new (fMemoryManager) SchemaValidator(0, fMemoryManager);
Khaled Noaman's avatar
Khaled Noaman committed
    initValidator(fSchemaValidator);

    // Create IdentityConstraint info
PeiYong Zhang's avatar
PeiYong Zhang committed
    fICHandler = new (fMemoryManager) IdentityConstraintHandler(this, fMemoryManager);

    // Create schemaLocation pair info
    fLocationPairs = new (fMemoryManager) ValueVectorOf<XMLCh*>(8, fMemoryManager);
    // create pools for undeclared elements
    fDTDElemNonDeclPool = new (fMemoryManager) NameIdPool<DTDElementDecl>(29, 128, fMemoryManager);
    fSchemaElemNonDeclPool = new (fMemoryManager) RefHash3KeysIdPool<SchemaElementDecl>(29, true, 128, fMemoryManager); 
    fAttDefRegistry = new (fMemoryManager) RefHashTableOf<unsigned int>
    (
Neil Graham's avatar
Neil Graham committed
        131, false, new (fMemoryManager)HashPtr(), fMemoryManager
    );
    fUndeclaredAttrRegistry = new (fMemoryManager) RefHashTableOf<unsigned int>
    (
        7, false, fMemoryManager
    );
    fUndeclaredAttrRegistryNS = new (fMemoryManager) RefHash2KeysTableOf<unsigned int>
    (
        7, false, fMemoryManager
    fPSVIAttrList = new (fMemoryManager) PSVIAttributeList(fMemoryManager);

    // use fDTDValidator as the default validator
    if (!fValidator)
        fValidator = fDTDValidator;
Khaled Noaman's avatar
Khaled Noaman committed
}

void IGXMLScanner::cleanUp()
{
    fMemoryManager->deallocate(fElemState); //delete [] fElemState;
Khaled Noaman's avatar
Khaled Noaman committed
    delete fRawAttrList;
    fMemoryManager->deallocate(fRawAttrColonList);
Khaled Noaman's avatar
Khaled Noaman committed
    delete fDTDValidator;
    delete fSchemaValidator;
PeiYong Zhang's avatar
PeiYong Zhang committed
    delete fICHandler;
    delete fDTDElemNonDeclPool;
    delete fSchemaElemNonDeclPool;
    delete fAttDefRegistry;
    delete fUndeclaredAttrRegistry;
    delete fUndeclaredAttrRegistryNS;
    delete fPSVIAttrList;
    delete fPSVIElement;
Khaled Noaman's avatar
Khaled Noaman committed
    delete fErrorStack;
Khaled Noaman's avatar
Khaled Noaman committed
}

// ---------------------------------------------------------------------------
//  IGXMLScanner: Private scanning methods
// ---------------------------------------------------------------------------

//  This method is called from scanStartTag() to handle the very raw initial
//  scan of the attributes. It just fills in the passed collection with
//  key/value pairs for each attribute. No processing is done on them at all.
Khaled Noaman's avatar
Khaled Noaman committed
IGXMLScanner::rawAttrScan(const   XMLCh* const                elemName
                          ,       RefVectorOf<KVStringPair>&  toFill
                          ,       bool&                       isEmpty)
{
    //  Keep up with how many attributes we've seen so far, and how many
    //  elements are available in the vector. This way we can reuse old
    //  elements until we run out and then expand it.
    XMLSize_t attCount = 0;
    XMLSize_t curVecSize = toFill.size();
Khaled Noaman's avatar
Khaled Noaman committed

    // Assume it is not empty
    isEmpty = false;

    //  We loop until we either see a /> or >, handling key/value pairs util
    //  we get there. We place them in the passed vector, which we will expand
    //  as required to hold them.
    while (true)
    {
        // Get the next character, which should be non-space
        XMLCh nextCh = fReaderMgr.peekNextChar();

        //  If the next character is not a slash or closed angle bracket,
        //  then it must be whitespace, since whitespace is required
        //  between the end of the last attribute and the name of the next
        //  one.
        //
        if (attCount)
        {
            if ((nextCh != chForwardSlash) && (nextCh != chCloseAngle))
            {
Tinny Ng's avatar
Tinny Ng committed
                if (fReaderMgr.getCurrentReader()->isWhitespace(nextCh))
Khaled Noaman's avatar
Khaled Noaman committed
                {
                    // Ok, skip by them and get another char
                    fReaderMgr.getNextChar();
                    fReaderMgr.skipPastSpaces();
                    nextCh = fReaderMgr.peekNextChar();
                }
                 else
                {
                    // Emit the error but keep on going
                    emitError(XMLErrs::ExpectedWhitespace);
                }
            }
        }

        //  Ok, here we first check for any of the special case characters.
        //  If its not one, then we do the normal case processing, which
        //  assumes that we've hit an attribute value, Otherwise, we do all
        //  the special case checks.
Tinny Ng's avatar
Tinny Ng committed
        if (!fReaderMgr.getCurrentReader()->isSpecialStartTagChar(nextCh))
Khaled Noaman's avatar
Khaled Noaman committed
        {
            //  Assume its going to be an attribute, so get a name from
            //  the input.
            int colonPosition;
            if (!fReaderMgr.getQName(fAttNameBuf, &colonPosition))
            {                
                if (fAttNameBuf.isEmpty())
                    emitError(XMLErrs::ExpectedAttrName);
                else
                    emitError(XMLErrs::InvalidAttrName, fAttNameBuf.getRawBuffer()); 
Khaled Noaman's avatar
Khaled Noaman committed
                fReaderMgr.skipPastChar(chCloseAngle);
                return attCount;
            }

            const XMLCh* curAttNameBuf = fAttNameBuf.getRawBuffer();

Khaled Noaman's avatar
Khaled Noaman committed
            // And next must be an equal sign
            if (!scanEq())
            {
                static const XMLCh tmpList[] =
                {
                    chSingleQuote, chDoubleQuote, chCloseAngle
                    , chOpenAngle, chForwardSlash, chNull
                };

                emitError(XMLErrs::ExpectedEqSign);

                //  Try to sync back up by skipping forward until we either
                //  hit something meaningful.
                const XMLCh chFound = fReaderMgr.skipUntilInOrWS(tmpList);

                if ((chFound == chCloseAngle) || (chFound == chForwardSlash))
                {
                    // Jump back to top for normal processing of these
                    continue;
                }
                else if ((chFound == chSingleQuote)
                      ||  (chFound == chDoubleQuote)
Tinny Ng's avatar
Tinny Ng committed
                      ||  fReaderMgr.getCurrentReader()->isWhitespace(chFound))
Khaled Noaman's avatar
Khaled Noaman committed
                {
                    // Just fall through assuming that the value is to follow
                }
                else if (chFound == chOpenAngle)
                {
                    // Assume a malformed tag and that new one is starting
                    emitError(XMLErrs::UnterminatedStartTag, elemName);
                    return attCount;
                }
                else
                {
                    // Something went really wrong
                    return attCount;
                }
            }

            //  Next should be the quoted attribute value. We just do a simple
            //  and stupid scan of this value. The only thing we do here
            //  is to expand entity references.
            if (!basicAttrValueScan(curAttNameBuf, fAttValueBuf))
Khaled Noaman's avatar
Khaled Noaman committed
            {
                static const XMLCh tmpList[] =
                {
                    chCloseAngle, chOpenAngle, chForwardSlash, chNull
                };

                emitError(XMLErrs::ExpectedAttrValue);

                //  It failed, so lets try to get synced back up. We skip
                //  forward until we find some whitespace or one of the
                //  chars in our list.
                const XMLCh chFound = fReaderMgr.skipUntilInOrWS(tmpList);

                if ((chFound == chCloseAngle)
                ||  (chFound == chForwardSlash)
Tinny Ng's avatar
Tinny Ng committed
                ||  fReaderMgr.getCurrentReader()->isWhitespace(chFound))
Khaled Noaman's avatar
Khaled Noaman committed
                {
                    //  Just fall through and process this attribute, though
                    //  the value will be "".
                }
                else if (chFound == chOpenAngle)
                {
                    // Assume a malformed tag and that new one is starting
                    emitError(XMLErrs::UnterminatedStartTag, elemName);
                    return attCount;
                }
                else
                {
                    // Something went really wrong
                    return attCount;
                }
            }

            //  And now lets add it to the passed collection. If we have not
            //  filled it up yet, then we use the next element. Else we add
            //  a new one.
            KVStringPair* curPair = 0;
            if (attCount >= curVecSize)
            {
                curPair = new (fMemoryManager) KVStringPair
Khaled Noaman's avatar
Khaled Noaman committed
                (
                    curAttNameBuf
                    , fAttNameBuf.getLen()
Khaled Noaman's avatar
Khaled Noaman committed
                    , fAttValueBuf.getRawBuffer()
                    , fAttValueBuf.getLen()
Khaled Noaman's avatar
Khaled Noaman committed
                );
Khaled Noaman's avatar
Khaled Noaman committed
            }
Khaled Noaman's avatar
Khaled Noaman committed
            {
                curPair = toFill.elementAt(attCount);                
                curPair->set
                (
                    curAttNameBuf,
                    fAttNameBuf.getLen(),
                    fAttValueBuf.getRawBuffer(),
                    fAttValueBuf.getLen()
            }

            if (attCount >= fRawAttrColonListSize) {
                resizeRawAttrColonList();
Khaled Noaman's avatar
Khaled Noaman committed
            }
            fRawAttrColonList[attCount] = colonPosition;
Khaled Noaman's avatar
Khaled Noaman committed

            // And bump the count of attributes we've gotten
            attCount++;

            // And go to the top again for another attribute
            continue;
        }

        //  It was some special case character so do all of the checks and
        //  deal with it.
        if (!nextCh)
            ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager);
Khaled Noaman's avatar
Khaled Noaman committed

        if (nextCh == chForwardSlash)
        {
            fReaderMgr.getNextChar();
            isEmpty = true;
            if (!fReaderMgr.skippedChar(chCloseAngle))
                emitError(XMLErrs::UnterminatedStartTag, elemName);
            break;
        }
        else if (nextCh == chCloseAngle)
        {
            fReaderMgr.getNextChar();
            break;
        }
        else if (nextCh == chOpenAngle)
        {
            //  Check for this one specially, since its going to be common
            //  and it is kind of auto-recovering since we've already hit the
            //  next open bracket, which is what we would have seeked to (and
            //  skipped this whole tag.)
            emitError(XMLErrs::UnterminatedStartTag, elemName);
            break;
        }
        else if ((nextCh == chSingleQuote) || (nextCh == chDoubleQuote))
        {
            //  Check for this one specially, which is probably a missing
            //  attribute name, e.g. ="value". Just issue expected name
            //  error and eat the quoted string, then jump back to the
            //  top again.
            emitError(XMLErrs::ExpectedAttrName);
            fReaderMgr.getNextChar();
            fReaderMgr.skipQuotedString(nextCh);
            fReaderMgr.skipPastSpaces();
            continue;
        }
    }

    return attCount;
}


//  This method will kick off the scanning of the primary content of the
//  document, i.e. the elements.
Khaled Noaman's avatar
Khaled Noaman committed
{
    //  Go into a loop until we hit the end of the root element, or we fall
    //  out because there is no root element.
    //
    //  We have to do kind of a deeply nested double loop here in order to
    //  avoid doing the setup/teardown of the exception handler on each
    //  round. Doing it this way we only do it when an exception actually
    //  occurs.
    bool gotData = true;
    bool inMarkup = false;
    while (gotData)
    {
        try
        {
            while (gotData)
            {
                //  Sense what the next top level token is. According to what
                //  this tells us, we will call something to handle that kind
                //  of thing.
                XMLSize_t orgReader;
Khaled Noaman's avatar
Khaled Noaman committed
                const XMLTokens curToken = senseNextToken(orgReader);

                //  Handle character data and end of file specially. Char data
                //  is not markup so we don't want to handle it in the loop
                //  below.
                if (curToken == Token_CharData)
                {
                    //  Scan the character data and call appropriate events. Let
                    //  him use our local character data buffer for efficiency.
                    scanCharData(fCDataBuf);
                    continue;
                }
                else if (curToken == Token_EOF)
                {
                    //  The element stack better be empty at this point or we
                    //  ended prematurely before all elements were closed.
                    if (!fElemStack.isEmpty())
                    {
                        const ElemStack::StackElem* topElem = fElemStack.popTop();
                        emitError
                        (
                            XMLErrs::EndedWithTagsOnStack
                            , topElem->fThisElement->getFullName()
                        );
                    }

                    // Its the end of file, so clear the got data flag
                    gotData = false;
                    continue;
                }

                // We are in some sort of markup now
                inMarkup = true;

                //  According to the token we got, call the appropriate
                //  scanning method.
                switch(curToken)
                {
                    case Token_CData :
                        // Make sure we are within content
                        if (fElemStack.isEmpty())
                            emitError(XMLErrs::CDATAOutsideOfContent);
                        scanCDSection();
                        break;

                    case Token_Comment :
                        scanComment();
                        break;

                    case Token_EndTag :
                        scanEndTag(gotData);
                        break;

                    case Token_PI :
                        scanPI();
                        break;

                    case Token_StartTag :
                        if (fDoNamespaces)
                            scanStartTagNS(gotData);
                        else
                            scanStartTag(gotData);
                        break;

                    default :
                        fReaderMgr.skipToChar(chOpenAngle);
                        break;
                }

                if (orgReader != fReaderMgr.getCurrentReaderNum())
                    emitError(XMLErrs::PartialMarkupInEntity);

                // And we are back out of markup again
                inMarkup = false;
            }
        }
        catch(const EndOfEntityException& toCatch)
        {
            //  If we were in some markup when this happened, then its a
            //  partial markup error.
            if (inMarkup)
                emitError(XMLErrs::PartialMarkupInEntity);

            // Send an end of entity reference event
            if (fDocHandler)
                fDocHandler->endEntityReference(toCatch.getEntity());

            inMarkup = false;
        }
    }

    // It went ok, so return success
    return true;
}


void IGXMLScanner::scanEndTag(bool& gotData)
{
    //  Assume we will still have data until proven otherwise. It will only
    //  ever be false if this is the end of the root element.
    gotData = true;

    //  Check if the element stack is empty. If so, then this is an unbalanced
    //  element (i.e. more ends than starts, perhaps because of bad text
    //  causing one to be skipped.)
    if (fElemStack.isEmpty())
    {
        emitError(XMLErrs::MoreEndThanStartTags);
        fReaderMgr.skipPastChar(chCloseAngle);
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Scan_UnbalancedStartEnd, fMemoryManager);
Khaled Noaman's avatar
Khaled Noaman committed
    }

    //  Pop the stack of the element we are supposed to be ending. Remember
    //  that we don't own this. The stack just keeps them and reuses them.
    unsigned int uriId = (fDoNamespaces)
        ? fElemStack.getCurrentURI() : fEmptyNamespaceId;

    // these get initialized below
    const ElemStack::StackElem* topElem = 0;    
    const XMLCh *elemName = 0;

    // Make sure that its the end of the element that we expect
    // special case for schema validation, whose element decls,
    // obviously don't contain prefix information
    if(fGrammarType == Grammar::SchemaGrammarType)
Khaled Noaman's avatar
Khaled Noaman committed
    {
        elemName = fElemStack.getCurrentSchemaElemName();
        topElem = fElemStack.topElement();         
Khaled Noaman's avatar
Khaled Noaman committed
    }
        topElem = fElemStack.topElement();         
        elemName = topElem->fThisElement->getFullName();
    }
    if (!fReaderMgr.skippedString(elemName))
Khaled Noaman's avatar
Khaled Noaman committed
    {
Khaled Noaman's avatar
Khaled Noaman committed
        (
            XMLErrs::ExpectedEndOfTagX
            , elemName
Khaled Noaman's avatar
Khaled Noaman committed
        );
        fReaderMgr.skipPastChar(chCloseAngle);
Khaled Noaman's avatar
Khaled Noaman committed

    // Make sure we are back on the same reader as where we started
    if (topElem->fReaderNum != fReaderMgr.getCurrentReaderNum())
        emitError(XMLErrs::PartialTagMarkupError);

    // Skip optional whitespace
    fReaderMgr.skipPastSpaces();

    // Make sure we find the closing bracket
    if (!fReaderMgr.skippedChar(chCloseAngle))
    {
        emitError
        (
            XMLErrs::UnterminatedEndTag
            , topElem->fThisElement->getFullName()
        );
    }

    if (fGrammarType == Grammar::SchemaGrammarType)
Khaled Noaman's avatar
Khaled Noaman committed
    {
        // reset error occurred
        fPSVIElemContext.fErrorOccurred = fErrorStack->pop();
Khaled Noaman's avatar
Khaled Noaman committed
        if (fValidate && topElem->fThisElement->isDeclared())
        {