/*
 * 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.
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/Janitor.hpp>
#include <xercesc/framework/XMLDocumentHandler.hpp>
#include <xercesc/framework/XMLSchemaDescription.hpp>
#include <xercesc/internal/XMLReader.hpp>
#include <xercesc/internal/XMLScanner.hpp>
#include <xercesc/internal/ElemStack.hpp>
#include <xercesc/validators/datatype/DatatypeValidatorFactory.hpp>
#include <xercesc/validators/datatype/ListDatatypeValidator.hpp>
#include <xercesc/validators/datatype/UnionDatatypeValidator.hpp>
#include <xercesc/validators/datatype/ENTITYDatatypeValidator.hpp>
#include <xercesc/validators/datatype/IDDatatypeValidator.hpp>
#include <xercesc/validators/datatype/IDREFDatatypeValidator.hpp>
#include <xercesc/validators/schema/SchemaSymbols.hpp>
#include <xercesc/validators/schema/SchemaValidator.hpp>
#include <xercesc/validators/schema/SubstitutionGroupComparator.hpp>
#include <xercesc/validators/schema/XercesGroupInfo.hpp>
#include <xercesc/validators/schema/XSDLocator.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/internal/XMLGrammarPoolImpl.hpp>

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  SchemaValidator: Constructors and Destructor
// ---------------------------------------------------------------------------
SchemaValidator::SchemaValidator( XMLErrorReporter* const errReporter
                                , MemoryManager* const    manager) :

    XMLValidator(errReporter)
    , fMemoryManager(manager)
    , fSchemaGrammar(0)
    , fGrammarResolver(0)
    , fXsiType(0)
    , fNil(false)
    , fCurrentDatatypeValidator(0)
    , fNotationBuf(0)
    , fDatatypeBuffer(1023, manager)
    , fTrailing(false)
    , fSeenId(false)
    , fTypeStack(0)
    , fMostRecentAttrValidator(0)
    , fErrorOccurred(false)
    , fElemIsSpecified(false)
{
    fTypeStack = new (fMemoryManager) ValueStackOf<ComplexTypeInfo*>(8, fMemoryManager);
}

SchemaValidator::~SchemaValidator()
{
    delete fXsiType;
    delete fTypeStack;

    if (fNotationBuf)
        delete fNotationBuf;
}

// ---------------------------------------------------------------------------
//  SchemaValidator: Implementation of the XMLValidator interface
// ---------------------------------------------------------------------------
int SchemaValidator::checkContent (XMLElementDecl* const elemDecl
                                 , QName** const         children
                                 , const unsigned int    childCount)
{
    fErrorOccurred = false;
    fElemIsSpecified = false;

    //
    //  Look up the element id in our element decl pool. This will get us
    //  the element decl in our own way of looking at them.
    //
    if (!elemDecl)
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Val_InvalidElemId, fMemoryManager);

    //
    //  Get the content spec type of this element. This will tell us what
    //  to do to validate it.
    //
    // the top of the type stack always knows best...
    ComplexTypeInfo* currType = fTypeStack->pop();

    const SchemaElementDecl::ModelTypes modelType = (currType)
            ? (SchemaElementDecl::ModelTypes)(currType->getContentType())
            : ((SchemaElementDecl*)elemDecl)->getModelType();

    if (modelType == SchemaElementDecl::Empty)
    {
        //
        //  We can do this one here. It cannot have any children. If it does
        //  we return 0 as the index of the first bad child.
        //
        if (childCount) {
            fErrorOccurred = true;
            return 0;
        }
    }
    else if ((modelType == SchemaElementDecl::Mixed_Simple)
         ||  (modelType == SchemaElementDecl::Mixed_Complex)
         ||  (modelType == SchemaElementDecl::Children))
    {
        // if nillable, it's an error to have value
        // XML Schema REC: Validation Rule: Element Locally Valid (Element)
        // 3.2.1 The element information item must have no
        // character or element information item [children].
        //
        if (fNil) {
            if (childCount > 0 || !XMLString::equals(fDatatypeBuffer.getRawBuffer(), XMLUni::fgZeroLenString)) {
                emitError(XMLValid::NilAttrNotEmpty, elemDecl->getFullName());
                fErrorOccurred = true;
            }
        }
        else {
            // Get the element's content model or fault it in
            XMLContentModel* elemCM = currType->getContentModel();

            // Ask it to validate and return its return
            unsigned int emptyNS = getScanner()->getEmptyNamespaceId();
            int result = elemCM->validateContent(children, childCount, emptyNS);
            if (result != -1) {
                result = elemCM->validateContentSpecial(children
                                                      , childCount
                                                      , emptyNS
                                                      , fGrammarResolver
                                                      , fGrammarResolver->getStringPool());
            }

            if(result != -1) {
                fErrorOccurred = true;
            }

            return result;
        }
    }
    else if (modelType == SchemaElementDecl::Simple || modelType == SchemaElementDecl::Any)
    {
        // Normally for SchemaElementDecl::Any, We pass no judgement on it and anything goes
        // but if there is a fXsiTypeValidator, we need to use it for validation
        if (modelType == SchemaElementDecl::Simple && childCount > 0) {
            emitError(XMLValid::SimpleTypeHasChild, elemDecl->getFullName());
            fErrorOccurred = true;
        }
        else
        {
            XMLCh* value = fDatatypeBuffer.getRawBuffer();
            XMLCh* elemDefaultValue = ((SchemaElementDecl*) elemDecl)->getDefaultValue();

			if (fCurrentDatatypeValidator)
            {
                if (fNil)
                {
                    if ((!XMLString::equals(value, XMLUni::fgZeroLenString))
                        || elemDefaultValue)
                    {
                        emitError(XMLValid::NilAttrNotEmpty, elemDecl->getFullName());
                        fErrorOccurred = true;
                    }
                }
                else
                {
                    DatatypeValidator::ValidatorType eleDefDVType = fCurrentDatatypeValidator->getType();
                    bool validateCanonical = false;
                    if (eleDefDVType == DatatypeValidator::NOTATION)
                    {
                        // if notation, need to bind URI to notation first
                        if (!fNotationBuf)
                            fNotationBuf = new (fMemoryManager) XMLBuffer(1023, fMemoryManager);

                        //  Make sure that this value maps to one of the
                        //  notation values in the enumList parameter. We don't have to
                        //  look it up in the notation pool (if a notation) because we
                        //  will look up the enumerated values themselves. If they are in
                        //  the notation pool (after the Grammar is parsed), then obviously
                        //  this value will be legal since it matches one of them.
                        int colonPos = -1;
                        unsigned int uriId = getScanner()->resolveQName(value, *fNotationBuf, ElemStack::Mode_Element, colonPos);
                        fNotationBuf->set(getScanner()->getURIText(uriId));
                        fNotationBuf->append(chColon);
                        fNotationBuf->append(&value[colonPos + 1]);
                        value = fNotationBuf->getRawBuffer();
                    }

                    if (elemDefaultValue)
                    {
                        if (XMLString::equals(value, XMLUni::fgZeroLenString))
                        {
                            fElemIsSpecified = true;
                            // if this element didn't specified any value
                            // use default value
                            if (getScanner()->getDocHandler())
                                getScanner()->getDocHandler()->docCharacters(elemDefaultValue, XMLString::stringLen(elemDefaultValue), false);

                            // Normally for default value, it has been validated already during TraverseSchema
                            // But if there was a xsi:type and this validator is fXsiTypeValidator,
                            // need to validate again
                            // we determine this if the current content dataype validator
                            // is neither the one in the element nor the one in the current
                            // complex type (if any)
                            if ((fCurrentDatatypeValidator != ((SchemaElementDecl*)elemDecl)->getDatatypeValidator())
                                && (!fTypeStack->peek() || (fCurrentDatatypeValidator != fTypeStack->peek()->getDatatypeValidator()))) {
                                value = elemDefaultValue;
                                validateCanonical = true;
                            }
                            else
                                value = 0;
                        }
                        else
                        {
                            // this element has specified some value
                            // if the flag is FIXED, then this value must be same as default value
                            if ((((SchemaElementDecl*)elemDecl)->getMiscFlags() & SchemaSymbols::XSD_FIXED) != 0)
                            {
                                if (fCurrentDatatypeValidator->compare(value, elemDefaultValue, fMemoryManager) != 0 )
                                {
                                    emitError(XMLValid::FixedDifferentFromActual, elemDecl->getFullName());
                                    fErrorOccurred = true;
                                }
                            }
                        }
                    }
                    else
                    {
                        // no default value, then check nillable
                        if (XMLString::equals(value, XMLUni::fgZeroLenString)
                            && (((SchemaElementDecl*)elemDecl)->getMiscFlags() & SchemaSymbols::XSD_NILLABLE) != 0)
                                value = 0;
                    }

                    if ((!fErrorOccurred) && value)
                    {
                        try {
                            fCurrentDatatypeValidator->validate(value, getScanner()->getValidationContext(), fMemoryManager);
                            if (validateCanonical) {
                                XMLCh* canonical = (XMLCh*) fCurrentDatatypeValidator->getCanonicalRepresentation(value, fMemoryManager);
                                ArrayJanitor<XMLCh> tempCanonical(canonical, fMemoryManager);
                                fCurrentDatatypeValidator->validate(canonical, getScanner()->getValidationContext(), fMemoryManager);
                            }
                        }
                        catch (XMLException& idve)
                        {
                            emitError (XMLValid::DatatypeError, idve.getCode(), idve.getType(), idve.getMessage());
                            fErrorOccurred = true;
                        }
                        catch(const OutOfMemoryException&) {
                            throw;
                        }
                        catch (...)
                        {
                            emitError(XMLValid::GenericError);
                            throw;
                        }
                    }
                }
            }
            else if (modelType == SchemaElementDecl::Simple)
            {
                emitError(XMLValid::NoDatatypeValidatorForSimpleType, elemDecl->getFullName());
                fErrorOccurred = true;
            }
            // modelType is any
            else if (elemDefaultValue)
            {
                if (XMLString::equals(value, XMLUni::fgZeroLenString))
                {
                    fElemIsSpecified = true;
                    // if this element didn't specified any value
                    // use default value
                    if (getScanner()->getDocHandler()) {
                        getScanner()->getDocHandler()->docCharacters(elemDefaultValue, XMLString::stringLen(elemDefaultValue), false);
                    }
                }
            }
        }
    }
    else
    {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_UnknownCMType, fMemoryManager);
    }

    // must rely on scanner to clear fDatatypeBuffer
    // since it may need to query its contents after this method completes
    fNil = false;
    fTrailing=false;
    fCurrentDatatypeValidator = 0;

    // Went ok, so return success
    return -1;
}

void SchemaValidator::faultInAttr (XMLAttr&    toFill, const XMLAttDef&  attDef)   const
{
    //
    //  At this level, we cannot set the URI id. So we just set it to zero
    //  and leave it at that. The scanner, who called us, will look at the
    //  prefix we stored (if any), resolve it, and store the URL id if any.
    //
    SchemaAttDef* schemaAttDef = (SchemaAttDef*) &attDef;
    QName* attName = schemaAttDef->getAttName();

    toFill.set
    (
          attName->getURI()
        , attName->getLocalPart()
        , attName->getPrefix()
        , schemaAttDef->getValue()
        , schemaAttDef->getType()
    );
}

void SchemaValidator::reset()
{
    fTrailing = false;
    fSeenId = false;
	fTypeStack->removeAllElements();
    delete fXsiType;
    fXsiType = 0;
    fCurrentDatatypeValidator = 0;
    fNil = false;
    fDatatypeBuffer.reset();
    fErrorOccurred = false;
}

bool SchemaValidator::requiresNamespaces() const
{
    return true;
}

void SchemaValidator::validateAttrValue (const XMLAttDef*      attDef
                                       , const XMLCh* const    attrValue
                                       , bool                  preValidation
                                       , const XMLElementDecl* elemDecl)
{
    fErrorOccurred = false;

    //turn on IdRefList checking
    getScanner()->getValidationContext()->toCheckIdRefList(true);

    //
    //  Get quick refs to lot of the stuff in the passed objects in
    //  order to simplify the code below, which will reference them very
    //  often.
    //
    XMLAttDef::AttTypes            type      = attDef->getType();
    const XMLAttDef::DefAttTypes   defType   = attDef->getDefaultType();

    //
    //  If the default type is fixed, then make sure the passed value maps
    //  to the fixed value.
    //
    //  If during preContentValidation, the value we are validating is the fixed value itself
    //  so no need to compare.
    //  Only need to do this for regular attribute value validation
    //
    if ((defType == XMLAttDef::Fixed || defType == XMLAttDef::Required_And_Fixed) && !preValidation)
    {
        const XMLCh* const valueText = attDef->getValue();
        if (!XMLString::equals(attrValue, valueText)) {
            emitError(XMLValid::NotSameAsFixedValue, attDef->getFullName(), attrValue, valueText);
            fErrorOccurred = true;
        }
    }

    // An empty string cannot be valid for non_CDATA any of the other types
    if (!attrValue[0] && type != XMLAttDef::Simple)
    {
        emitError(XMLValid::InvalidEmptyAttValue, attDef->getFullName());
        // accords with original DOMTypeInfo implementation, but this does not feel right.
        fMostRecentAttrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE);
        fErrorOccurred = true;
        return;
    }

    DatatypeValidator* attDefDV = ((SchemaAttDef*) attDef)->getDatatypeValidator();
    if (!attDefDV) {
        emitError(XMLValid::NoDatatypeValidatorForAttribute, attDef->getFullName());
        fErrorOccurred = true;
    }
    else {
        DatatypeValidator::ValidatorType attDefDVType = attDefDV->getType();
        ValidationContext *context = getScanner()->getValidationContext();
        try {

            // first, if notation, need to bind URI to notation first
            if (attDefDVType == DatatypeValidator::NOTATION)
            {
                //
                //  Make sure that this value maps to one of the
                //  notation values in the enumList parameter. We don't have to
                //  look it up in the notation pool (if a notation) because we
                //  will look up the enumerated values themselves. If they are in
                //  the notation pool (after the Grammar is parsed), then obviously
                //  this value will be legal since it matches one of them.
                //
                XMLBuffer notationBuf(1023, fMemoryManager);
                int colonPos = -1;
                unsigned int uriId = getScanner()->resolveQName(attrValue, notationBuf, ElemStack::Mode_Element, colonPos);
                notationBuf.set(getScanner()->getURIText(uriId));
                notationBuf.append(chColon);
                notationBuf.append(&attrValue[colonPos + 1]);

                attDefDV->validate(notationBuf.getRawBuffer()
                                 , context
                                 , fMemoryManager);
            }
            else {
                attDefDV->validate(attrValue
                                 , context
                                 , fMemoryManager);
            }

        }
        catch (XMLException& idve) {
            fErrorOccurred = true;
            emitError (XMLValid::DatatypeError, idve.getCode(), idve.getType(), idve.getMessage());       
        }
        catch(const OutOfMemoryException&)
        {
            throw;
        }
        catch (...) {
            emitError(XMLValid::GenericError);
            fMostRecentAttrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE);
            fErrorOccurred = true;
            throw;
        } 
        fMostRecentAttrValidator = attDefDV;
        // now we can look for ID's, entities, ...

        // set up the entitydeclpool in ENTITYDatatypeValidator
        // and the idreflist in ID/IDREFDatatypeValidator

        // indicate if this attribute is of type ID
        bool thisIsAnId = false;

        if (attDefDVType == DatatypeValidator::List) {
            DatatypeValidator* itemDTV = ((ListDatatypeValidator*)attDefDV)->getItemTypeDTV();
            DatatypeValidator::ValidatorType itemDTVType = itemDTV->getType();
            if (itemDTVType == DatatypeValidator::ID) {
                thisIsAnId = true;
            }
            else if (itemDTVType == DatatypeValidator::IDREF) {
                // if in prevalidatoin, do not add attDef to IDREFList
                if (preValidation)
                    //todo: when to setIdRefList back to non-null
                    getScanner()->getValidationContext()->toCheckIdRefList(false);
            }
        }
        else if (attDefDVType == DatatypeValidator::Union) {
            DatatypeValidator *memberDTV = context->getValidatingMemberType();
            // actual type for DOMTypeInfo is memberDTV
            fMostRecentAttrValidator = memberDTV;
            // no member datatype validator if there was an error
            if(memberDTV)
            {
                DatatypeValidator::ValidatorType memberDTVType = memberDTV->getType();
                if (memberDTVType == DatatypeValidator::ID) {
                    thisIsAnId = true;
                }
                else if (memberDTVType == DatatypeValidator::IDREF) {
                    // if in prevalidatoin, do not add attDef to IDREFList
                    if (preValidation)
                        getScanner()->getValidationContext()->toCheckIdRefList(false);
                }
            }
        }
        else if (attDefDVType == DatatypeValidator::ID) {
            thisIsAnId = true;
        }
        else if (attDefDVType == DatatypeValidator::IDREF) {
            // if in prevalidation, do not add attDef to IDREFList
            if (preValidation)
                getScanner()->getValidationContext()->toCheckIdRefList(false);
        }
        if (thisIsAnId) {
            if (fSeenId) {
                emitError
                (
                    XMLValid::MultipleIdAttrs
                    , elemDecl->getFullName()
                );
                fErrorOccurred = true;
            }
            else
                fSeenId = true;
        }

    }

    if(fErrorOccurred) {
        fMostRecentAttrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE);
    }
    fTrailing = false;


}

void SchemaValidator::validateElement(const   XMLElementDecl*  elemDef)
{
    ComplexTypeInfo* elemTypeInfo = ((SchemaElementDecl*)elemDef)->getComplexTypeInfo();
    fTypeStack->push(elemTypeInfo);
    fCurrentDatatypeValidator = (elemTypeInfo)
            ? elemTypeInfo->getDatatypeValidator()
            : ((SchemaElementDecl*)elemDef)->getDatatypeValidator();

    fErrorOccurred = false;

    if (fXsiType) {
        // handle "xsi:type" right here
        DatatypeValidator *xsiTypeDV = 0;
        unsigned int uri = fXsiType->getURI();
        const XMLCh* localPart = fXsiType->getLocalPart();

        if (uri != XMLElementDecl::fgInvalidElemId &&
            uri != XMLElementDecl::fgPCDataElemId &&
            uri != XMLContentModel::gEpsilonFakeId &&
            uri != XMLContentModel::gEOCFakeId) {

            // retrieve Grammar for the uri
            const XMLCh* uriStr = getScanner()->getURIText(uri);
            SchemaGrammar* sGrammar = (SchemaGrammar*) fGrammarResolver->getGrammar(uriStr);
            if (!sGrammar) {

                // Check built-in simple types
                if (XMLString::equals(uriStr, SchemaSymbols::fgURI_SCHEMAFORSCHEMA)) {

                    xsiTypeDV = fGrammarResolver->getDatatypeValidator(uriStr, localPart);

                    if (!xsiTypeDV) {
                        emitError(XMLValid::BadXsiType, fXsiType->getRawName());
                        fErrorOccurred = true;
                    }
                    else {
                        if (elemTypeInfo || (fCurrentDatatypeValidator  
                                && !fCurrentDatatypeValidator->isSubstitutableBy(xsiTypeDV))) {
                            // the type is not derived from ancestor
                            emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                            fErrorOccurred = true;
                        }
                        else {
                            // the type is derived from ancestor
                            if (((SchemaElementDecl*)elemDef)->getBlockSet() == SchemaSymbols::XSD_RESTRICTION) {
                                emitError(XMLValid::NoSubforBlock, fXsiType->getRawName(), elemDef->getFullName());
                                fErrorOccurred = true;
                            }
                            if (elemDef->hasAttDefs()) {
                                // if we have an attribute but xsi:type's type is simple, we have a problem...
                                emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                                fErrorOccurred = true;
                            }
                        }
                        fCurrentDatatypeValidator = xsiTypeDV;
                    }
                }
                else {
                    // Grammar not found
                    emitError(XMLValid::GrammarNotFound, uriStr);
                    fErrorOccurred = true;
                }
            }
            else if (sGrammar->getGrammarType() != Grammar::SchemaGrammarType) {
                emitError(XMLValid::GrammarNotFound, uriStr);
                fErrorOccurred = true;
            }
            else {
                // retrieve complexType registry and DatatypeValidator registry
                RefHashTableOf<ComplexTypeInfo>* complexTypeRegistry = sGrammar->getComplexTypeRegistry();
                if (!complexTypeRegistry) {
                    emitError(XMLValid::BadXsiType, fXsiType->getRawName());
                    fErrorOccurred = true;
                }
                else {

                    // retrieve the typeInfo specified in xsi:type
                    XMLBuffer aBuffer(1023, fMemoryManager);
                    aBuffer.set(uriStr);
                    aBuffer.append(chComma);
                    aBuffer.append(localPart);
                    ComplexTypeInfo* typeInfo = complexTypeRegistry->get(aBuffer.getRawBuffer());

                    if (typeInfo) {
                        // typeInfo is found
                        if (typeInfo->getAbstract()) {
                            emitError(XMLValid::NoAbstractInXsiType, aBuffer.getRawBuffer());
                            fErrorOccurred = true;
                        }
                        else
                        {
                            if (elemTypeInfo)
                            {
                                ComplexTypeInfo* tempType = typeInfo;
                                while (tempType) {
                                    if (tempType == elemTypeInfo)
                                        break;
                                    tempType = tempType->getBaseComplexTypeInfo();
                                }

                                if (!tempType) {
                                    emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                                    fErrorOccurred = true;
                                }
                                else {
                                    int derivationMethod = typeInfo->getDerivedBy();
                                    if ((((SchemaElementDecl*)elemDef)->getBlockSet() & derivationMethod) != 0) {
                                        emitError(XMLValid::NoSubforBlock, fXsiType->getRawName(), elemDef->getFullName());
                                        fErrorOccurred = true;
                                    }
                                }
                            }
                            else
                            {
                                // if the original type is a simple type, check derivation ok.
                                if (fCurrentDatatypeValidator && !fCurrentDatatypeValidator->isSubstitutableBy(typeInfo->getDatatypeValidator())) {
                                    // the type is not derived from ancestor
                                    emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                                    fErrorOccurred = true;
                                }
                            }

                            if (!fErrorOccurred)
                            {
                                fTypeStack->pop();
                                fTypeStack->push(typeInfo);
                                fCurrentDatatypeValidator = typeInfo->getDatatypeValidator();
                            }
                        }
                    }
                    else
                    {
                        // typeInfo not found
                        xsiTypeDV = fGrammarResolver->getDatatypeValidator(uriStr, localPart);

                        if (!xsiTypeDV) {
                            emitError(XMLValid::BadXsiType, fXsiType->getRawName());
                            fErrorOccurred = true;
                        }
                        else {
                            if (fCurrentDatatypeValidator && !fCurrentDatatypeValidator->isSubstitutableBy(xsiTypeDV)) {
                                // the type is not derived from ancestor
                                emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                                fErrorOccurred = true;
                            }
                            else {
                                // the type is derived from ancestor
                                if (((SchemaElementDecl*)elemDef)->getBlockSet() == SchemaSymbols::XSD_RESTRICTION) {
                                    emitError(XMLValid::NoSubforBlock, fXsiType->getRawName(), elemDef->getFullName());
                                    fErrorOccurred = true;
                                }
                                if (elemDef->hasAttDefs()) {
                                    // if we have an attribute but xsi:type's type is simple, we have a problem...
                                    emitError(XMLValid::NonDerivedXsiType, fXsiType->getRawName(), elemDef->getFullName());
                                    fErrorOccurred = true;
                                }
                            }

                            fCurrentDatatypeValidator = xsiTypeDV;
                        }
                    }
                }
            }
        }

        delete fXsiType;
        fXsiType = 0;
    }
    else {
        //
        // xsi:type was not specified...
        // If the corresponding type is abstract, detect an error
        //
        if (elemTypeInfo && elemTypeInfo->getAbstract()) {
            emitError(XMLValid::NoUseAbstractType, elemDef->getFullName());
            fErrorOccurred = true;
        }
    }

    //
    // Check whether this element is abstract.  If so, an error
    //
    int miscFlags = ((SchemaElementDecl*)elemDef)->getMiscFlags();
    if ((miscFlags & SchemaSymbols::XSD_ABSTRACT) != 0) {
        emitError(XMLValid::NoDirectUseAbstractElement, elemDef->getFullName());
        fErrorOccurred = true;
    }

    //
    // Check whether this element allows Nillable
    //
    if (fNil && (miscFlags & SchemaSymbols::XSD_NILLABLE) == 0 ) {
        fNil = false;
        emitError(XMLValid::NillNotAllowed, elemDef->getFullName());
        fErrorOccurred = true;
    }

    fDatatypeBuffer.reset();
    fTrailing = false;
    fSeenId = false;
}

void SchemaValidator::preContentValidation(bool,
                                           bool validateDefAttr)
{
    //  Lets go through all the grammar in the GrammarResolver
    //    and validate those that has not been validated yet
    //
    //  Lets enumerate all of the elements in the element decl pool
    //    and put out an error for any that did not get declared.
    //    We also check all of the attributes as well.
    //
    //  And enumerate all the complextype info in the grammar
    //    and do Unique Particle Attribution Checking

    RefHashTableOfEnumerator<Grammar> grammarEnum = fGrammarResolver->getGrammarEnumerator();
    while (grammarEnum.hasMoreElements())
    {
        SchemaGrammar& sGrammar = (SchemaGrammar&) grammarEnum.nextElement();
        if (sGrammar.getGrammarType() != Grammar::SchemaGrammarType || sGrammar.getValidated())
             continue;

        sGrammar.setValidated(true);

        RefHash3KeysIdPoolEnumerator<SchemaElementDecl> elemEnum = sGrammar.getElemEnumerator();

        while (elemEnum.hasMoreElements())
        {
            SchemaElementDecl& curElem = elemEnum.nextElement();

            //  First check if declared or not
            //
            //  See if this element decl was ever marked as declared. If
            //  not, then put out an error. In some cases its just
            //  a warning, such as being referenced in a content model.
            //
            const SchemaElementDecl::CreateReasons reason = curElem.getCreateReason();

            if (reason != XMLElementDecl::Declared)
            {
                if (reason == XMLElementDecl::AttList)
                {
                    getScanner()->emitError
                    (
                        XMLErrs::UndeclaredElemInAttList
                        , curElem.getFullName()
                    );
                }
                 else if (reason == XMLElementDecl::AsRootElem)
                {
                    emitError
                    (
                        XMLValid::UndeclaredElemInDocType
                        , curElem.getFullName()
                    );
                }
                 else if (reason == XMLElementDecl::InContentModel)
                {
                    getScanner()->emitError
                    (
                        XMLErrs::UndeclaredElemInCM
                        , curElem.getFullName()
                    );
                }
                else
                {
                }
            }

            //
            //  Then check all of the attributes of the current element.
            //  We check for:
            //
            //  1) Multiple ID attributes
            //  2) That all of the default values of attributes are
            //      valid for their type.
            //  3) That for any notation types, that their lists
            //      of possible values refer to declared notations.
            //
            if (curElem.hasAttDefs()) {
                XMLAttDefList& attDefList = curElem.getAttDefList();
                bool seenId = false;

                for(unsigned int i=0; i<attDefList.getAttDefCount(); i++)
                {
                    const XMLAttDef& curAttDef = attDefList.getAttDef(i);

                    if (curAttDef.getType() == XMLAttDef::ID)
                    {
                        if (seenId)
                        {
                            emitError
                            (
                                XMLValid::MultipleIdAttrs
                                , curElem.getFullName()
                            );
                            break;
                        }

                        seenId = true;
                    }
                     else if (curAttDef.getType() == XMLAttDef::Notation && curAttDef.getEnumeration())
                    {
                        //
                        //  We need to verify that all of its possible values
                        //  (in the enum list) refer to valid notations.
                        //
                        XMLCh* list = XMLString::replicate(curAttDef.getEnumeration(), fMemoryManager);
                        ArrayJanitor<XMLCh> janList(list, fMemoryManager);

                        //
                        //  Search forward for a space or a null. If a null,
                        //  we are done. If a space, cap it and look it up.
                        //
                        bool    breakFlag = false;
                        XMLCh*  listPtr = list;
                        XMLCh*  lastPtr = listPtr;
                        while (true)
                        {
                            while (*listPtr && (*listPtr != chSpace))
                                listPtr++;

                            //
                            //  If at the end, indicate we need to break after
                            //  this one. Else, cap it off here.
                            //
                            if (!*listPtr)
                                breakFlag = true;
                            else
                                *listPtr = chNull;

                            if (!sGrammar.getNotationDecl(lastPtr))
                            {
                                emitError
                                (
                                    XMLValid::UnknownNotRefAttr
                                    , curAttDef.getFullName()
                                    , lastPtr
                                );
                            }

                            // Break out if we hit the end last time
                            if (breakFlag)
                                break;

                            // Else move upwards and try again
                            listPtr++;
                            lastPtr = listPtr;
                        }
                    }

                    // If it has a default/fixed value, then validate it
                    if (validateDefAttr && curAttDef.getValue())
                    {
                        validateAttrValue
                        (
                            &curAttDef
                            , curAttDef.getValue()
                            , true
                            , &curElem
                        );
                    }
                }
            }
        }

        //  For each complex type info, check the Unique Particle Attribution
        if (getScanner()->getValidationSchemaFullChecking()) {
            RefHashTableOf<ComplexTypeInfo>* complexTypeRegistry = sGrammar.getComplexTypeRegistry();

            RefHashTableOfEnumerator<ComplexTypeInfo> complexTypeEnum(complexTypeRegistry, false, fMemoryManager);
            while (complexTypeEnum.hasMoreElements())
            {
                ComplexTypeInfo& curTypeInfo = complexTypeEnum.nextElement();
                curTypeInfo.checkUniqueParticleAttribution(&sGrammar, fGrammarResolver, fGrammarResolver->getStringPool(), this);
                checkParticleDerivation(&sGrammar, &curTypeInfo);
                checkRefElementConsistency(&sGrammar, &curTypeInfo);
            }

            RefHashTableOf<XercesGroupInfo>* groupInfoRegistry = sGrammar.getGroupInfoRegistry();
            RefHashTableOfEnumerator<XercesGroupInfo> groupEnum(groupInfoRegistry, false, fMemoryManager);

            while (groupEnum.hasMoreElements()) {

                XercesGroupInfo& curGroup = groupEnum.nextElement();
                XercesGroupInfo* baseGroup = curGroup.getBaseGroup();

                if (baseGroup) {
                    try {
                        checkParticleDerivationOk(&sGrammar, curGroup.getContentSpec(), curGroup.getScope(),
                                                  baseGroup->getContentSpec(), baseGroup->getScope());
                    }
                    catch (const XMLException& excep) {
                        fSchemaErrorReporter.emitError(excep, curGroup.getLocator());
					}
                }

                if (curGroup.getCheckElementConsistency())
                    checkRefElementConsistency(&sGrammar, 0, &curGroup);
            }
        }
    }
}

void SchemaValidator::postParseValidation()
{
    //
    //  At this time, there is nothing to do here. The scanner itself handles
    //  ID/IDREF validation, since that is the same no matter what kind of
    //  validator.
    //
}

// ---------------------------------------------------------------------------
//  SchemaValidator: Validator method
// ---------------------------------------------------------------------------
// Do Schema Normalization depends on the WhiteSpace Facet
// preserve : No normalization is done
// replace  : All occurrences of #x9 (tab), #xA (linefeed) and #xD (carriage return)
//            are replaced with #x20 (space).
// collapse : Subsequent to the replacements specified above under replace,
//            contiguous sequences of #x20s are collapsed to a single #x20,
//            and initial and/or final #x20s are deleted.
//
void SchemaValidator::normalizeWhiteSpace(DatatypeValidator* dV, const XMLCh* const value, XMLBuffer& toFill)
{
    short wsFacet = dV->getWSFacet();

    toFill.reset();

    //empty string
    if (!*value)
        return;

    enum States
    {
        InWhitespace
        , InContent
    };

    States curState = InContent;

    //  Loop through the chars of the source value and normalize it
    //  according to the whitespace facet
    bool firstNonWS = false;
    XMLCh nextCh;
    const XMLCh* srcPtr = value;
    XMLReader* fCurReader = getReaderMgr()->getCurrentReader();

    if ((wsFacet==DatatypeValidator::COLLAPSE) && fTrailing) {
        nextCh = *srcPtr;
        if (!fCurReader->isWhitespace(nextCh))
        toFill.append(chSpace);
    }

    while (*srcPtr)
    {
        nextCh = *srcPtr;
        if (wsFacet == DatatypeValidator::REPLACE)
        {
            if (fCurReader->isWhitespace(nextCh))
                nextCh = chSpace;
        }
        else // COLLAPSE case
        {
            if (curState == InWhitespace)
            {
                if (!fCurReader->isWhitespace(nextCh))
                {
                    if (firstNonWS)
                        toFill.append(chSpace);
                    curState = InContent;
                    firstNonWS = true;
                }
                else
                {
                    srcPtr++;
                    continue;
                }
            }
            else if (curState == InContent)
            {
                if (fCurReader->isWhitespace(nextCh))
                {
                    curState = InWhitespace;
                    srcPtr++;
                    continue;
                }
                firstNonWS = true;
            }
        }
        // Add this char to the target buffer
        toFill.append(nextCh);

        // And move up to the next character in the source
        srcPtr++;
    }
    if (fCurReader->isWhitespace(*(srcPtr-1)))
        fTrailing = true;
    else
        fTrailing = false;
}


// ---------------------------------------------------------------------------
//  SchemaValidator: Particle Derivation Checking
// ---------------------------------------------------------------------------
void SchemaValidator::checkRefElementConsistency(SchemaGrammar* const currentGrammar,
                                                 const ComplexTypeInfo* const curTypeInfo,
                                                 const XercesGroupInfo* const curGroup) {

    unsigned int elemCount = (curTypeInfo) ? curTypeInfo->elementCount() : curGroup->elementCount();
    int elemScope = (curTypeInfo) ? curTypeInfo->getScopeDefined() : curGroup->getScope();
    XSDLocator* typeInfoLocator = (curTypeInfo) ? curTypeInfo->getLocator() : curGroup->getLocator();

    for (unsigned int i=0; i < elemCount; i++) {

        const SchemaElementDecl* elemDecl = (curTypeInfo) ? curTypeInfo->elementAt(i) : curGroup->elementAt(i);

        if (elemDecl->isGlobalDecl()) {

            unsigned int elemURI = elemDecl->getURI();
            const XMLCh* elemName = elemDecl->getBaseName();
            const SchemaElementDecl* other = (SchemaElementDecl*)
                currentGrammar->getElemDecl(elemURI, elemName, 0, elemScope);

            if (other
                && (elemDecl->getComplexTypeInfo() != other->getComplexTypeInfo() ||
                    elemDecl->getDatatypeValidator() != other->getDatatypeValidator())) {
                fSchemaErrorReporter.emitError(XMLErrs::DuplicateElementDeclaration,
                                               XMLUni::fgXMLErrDomain, typeInfoLocator, elemName, 0, 0, 0, fMemoryManager);
                continue;
            }

            RefHash2KeysTableOf<ElemVector>* validSubsGroups = currentGrammar->getValidSubstitutionGroups();
            ValueVectorOf<SchemaElementDecl*>* subsElements = validSubsGroups->get(elemName, elemURI);

            if (subsElements) {

                unsigned subsElemSize = subsElements->size();

                for (unsigned int j=0; j < subsElemSize; j++) {

                    SchemaElementDecl* subsElem = subsElements->elementAt(j);
                    const XMLCh* subsElemName = subsElem->getBaseName();
                    other = (SchemaElementDecl*)
                        currentGrammar->getElemDecl(subsElem->getURI(), subsElemName, 0, elemScope);

                    if (other
                        && (subsElem->getComplexTypeInfo() != other->getComplexTypeInfo()
                            || subsElem->getDatatypeValidator() != other->getDatatypeValidator())) {
                        fSchemaErrorReporter.emitError(XMLErrs::DuplicateElementDeclaration,
                                                       XMLUni::fgXMLErrDomain, typeInfoLocator, elemName, 0, 0, 0, fMemoryManager);
                    }
                }
            }
        }
    }
}

// ---------------------------------------------------------------------------
//  SchemaValidator: Particle Derivation Checking
// ---------------------------------------------------------------------------
void SchemaValidator::checkParticleDerivation(SchemaGrammar* const currentGrammar,
                                              const ComplexTypeInfo* const curTypeInfo) {

    ComplexTypeInfo* baseTypeInfo = 0;
    ContentSpecNode* curSpecNode = 0;

    if (curTypeInfo->getDerivedBy() == SchemaSymbols::XSD_RESTRICTION
        && ((baseTypeInfo = curTypeInfo->getBaseComplexTypeInfo()) != 0)
        && ((curSpecNode = curTypeInfo->getContentSpec()) != 0)) {

        try {
            checkParticleDerivationOk(currentGrammar, curSpecNode,
                                      curTypeInfo->getScopeDefined(),
                                      baseTypeInfo->getContentSpec(),
                                      baseTypeInfo->getScopeDefined(), baseTypeInfo);
        }
        catch (const XMLException& excep) {
            fSchemaErrorReporter.emitError(excep, curTypeInfo->getLocator());
        }
    }
}

ContentSpecNode* SchemaValidator::getNonUnaryGroup(ContentSpecNode* const pNode) {

    int pNodeType = (pNode->getType() & 0x0f);
    if (pNodeType == ContentSpecNode::Leaf
        || pNodeType == ContentSpecNode::Any
        || pNodeType == ContentSpecNode::Any_Other
        || pNodeType == ContentSpecNode::Any_NS)
        return pNode;

    if (pNode->getMinOccurs() == 1 && pNode->getMaxOccurs() == 1
        && pNode->getFirst() && !pNode->getSecond())
        return getNonUnaryGroup(pNode->getFirst());

    return pNode;
}

void SchemaValidator::checkParticleDerivationOk(SchemaGrammar* const aGrammar,
                                                ContentSpecNode* const curNode,
                                                const int derivedScope,
                                                ContentSpecNode* const baseNode,
                                                const int baseScope,
                                                const ComplexTypeInfo* const baseInfo,
                                                const bool toCheckOccurence) {

    // Check for pointless occurrences of all, choice, sequence.  The result is
    // the contentspec which is not pointless. If the result is a non-pointless
    // group, Vector is filled  in with the children of interest
    if (curNode && !baseNode)
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_EmptyBase, fMemoryManager);

    if (!curNode)
        return;

    ContentSpecNode* curSpecNode = getNonUnaryGroup(curNode);
    ContentSpecNode* baseSpecNode = getNonUnaryGroup(baseNode);
    ValueVectorOf<ContentSpecNode*> curVector(8, fMemoryManager);
    ValueVectorOf<ContentSpecNode*> baseVector(8, fMemoryManager);
    ContentSpecNode::NodeTypes curNodeType = curSpecNode->getType();
    ContentSpecNode::NodeTypes baseNodeType = baseSpecNode->getType();

    if ((curNodeType & 0x0f) == ContentSpecNode::Sequence ||
        (curNodeType & 0x0f) == ContentSpecNode::Choice ||
        curNodeType == ContentSpecNode::All) {
        curSpecNode = checkForPointlessOccurrences(curSpecNode, curNodeType, &curVector);
    }

    if ((baseNodeType & 0x0f) == ContentSpecNode::Sequence ||
        (baseNodeType & 0x0f) == ContentSpecNode::Choice ||
        baseNodeType == ContentSpecNode::All) {
        baseSpecNode = checkForPointlessOccurrences(baseSpecNode, baseNodeType, &baseVector);
    }

    curNodeType = curSpecNode->getType();
    baseNodeType = baseSpecNode->getType();

    switch (curNodeType & 0x0f) {
    case ContentSpecNode::Leaf:
        {
            switch (baseNodeType & 0x0f) {
            case ContentSpecNode::Leaf:
                {
                    checkNameAndTypeOK(aGrammar, curSpecNode, derivedScope, baseSpecNode, baseScope, baseInfo);
                    return;
                }
            case ContentSpecNode::Any:
            case ContentSpecNode::Any_Other:
            case ContentSpecNode::Any_NS:
                {
                    checkNSCompat(curSpecNode, baseSpecNode, toCheckOccurence);
                    return;
                }
            case ContentSpecNode::Choice:
            case ContentSpecNode::Sequence:
            case ContentSpecNode::All:
                {
                    checkRecurseAsIfGroup(aGrammar, curSpecNode, derivedScope,
                                          baseSpecNode, baseScope, &baseVector, baseInfo);
                    return;
                }
            default:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_InvalidContentType, fMemoryManager);
                }
            }		
        }
    case ContentSpecNode::Any:
    case ContentSpecNode::Any_Other:
    case ContentSpecNode::Any_NS:
        {
            switch (baseNodeType & 0x0f) {
            case ContentSpecNode::Any:
            case ContentSpecNode::Any_Other:
            case ContentSpecNode::Any_NS:
                {
                     checkNSSubset(curSpecNode, baseSpecNode);
                     return;
                }
            case ContentSpecNode::Choice:
            case ContentSpecNode::Sequence:
            case ContentSpecNode::All:
            case ContentSpecNode::Leaf:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_ForbiddenRes1, fMemoryManager);
                }
            default:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_InvalidContentType, fMemoryManager);
                }
            }
        }
    case ContentSpecNode::All:
        {
            switch (baseNodeType & 0x0f) {
            case ContentSpecNode::Any:
            case ContentSpecNode::Any_Other:
            case ContentSpecNode::Any_NS:
                {
                    checkNSRecurseCheckCardinality(aGrammar, curSpecNode, &curVector, derivedScope, baseSpecNode, toCheckOccurence);
                    return;
                }
            case ContentSpecNode::All:
                {
                    checkRecurse(aGrammar, curSpecNode, derivedScope, &curVector,
                                 baseSpecNode, baseScope, &baseVector, baseInfo);
                    return;
                }
            case ContentSpecNode::Choice:
            case ContentSpecNode::Sequence:
            case ContentSpecNode::Leaf:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_ForbiddenRes2, fMemoryManager);
                }
            default:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_InvalidContentType, fMemoryManager);
                }
            }
        }
    case ContentSpecNode::Choice:
        {
            switch (baseNodeType & 0x0f) {
            case ContentSpecNode::Any:
            case ContentSpecNode::Any_Other:
            case ContentSpecNode::Any_NS:
                {
                    checkNSRecurseCheckCardinality(aGrammar, curSpecNode, &curVector, derivedScope, baseSpecNode, toCheckOccurence);
                    return;
                }
            case ContentSpecNode::Choice:
                {
                    checkRecurse(aGrammar, curSpecNode, derivedScope, &curVector,
                                 baseSpecNode, baseScope, &baseVector, baseInfo, true);
                    return;
                }
            case ContentSpecNode::All:
            case ContentSpecNode::Sequence:
            case ContentSpecNode::Leaf:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_ForbiddenRes3, fMemoryManager);
                }
            default:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_InvalidContentType, fMemoryManager);
                }
            }
        }
    case ContentSpecNode::Sequence:
        {
            switch (baseNodeType & 0x0f) {
            case ContentSpecNode::Any:
            case ContentSpecNode::Any_Other:
            case ContentSpecNode::Any_NS:
                {
                    checkNSRecurseCheckCardinality(aGrammar, curSpecNode, &curVector, derivedScope, baseSpecNode, toCheckOccurence);
                    return;
                }
            case ContentSpecNode::All:
                {
                    checkRecurseUnordered(aGrammar, curSpecNode, &curVector, derivedScope,
                                          baseSpecNode, &baseVector, baseScope, baseInfo);
                    return;
                }
            case ContentSpecNode::Sequence:
                {
                    checkRecurse(aGrammar, curSpecNode, derivedScope, &curVector,
                                 baseSpecNode, baseScope, &baseVector, baseInfo);
                    return;
                }
            case ContentSpecNode::Choice:
                {
                    checkMapAndSum(aGrammar, curSpecNode, &curVector, derivedScope,
                                   baseSpecNode, &baseVector, baseScope, baseInfo);
                    return;
                }
            case ContentSpecNode::Leaf:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_ForbiddenRes4, fMemoryManager);
                }
            default:
                {
                    ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_InvalidContentType, fMemoryManager);
                }
            }
        }
    }
}

ContentSpecNode*
SchemaValidator::checkForPointlessOccurrences(ContentSpecNode* const specNode,
                                              const ContentSpecNode::NodeTypes nodeType,
                                              ValueVectorOf<ContentSpecNode*>* const nodes) {

    ContentSpecNode* rightNode = specNode->getSecond();
    int min = specNode->getMinOccurs();
    int max = specNode->getMaxOccurs();

    if (!rightNode) {

         gatherChildren(nodeType, specNode->getFirst(), nodes);

         if (nodes->size() == 1 && min == 1 && max == 1) {
            return nodes->elementAt(0);
        }

        return specNode;
    }

    gatherChildren(nodeType, specNode->getFirst(), nodes);
    gatherChildren(nodeType, rightNode, nodes);

    return specNode;
}

void SchemaValidator::gatherChildren(const ContentSpecNode::NodeTypes parentNodeType,
                                    ContentSpecNode* const specNode,
                                    ValueVectorOf<ContentSpecNode*>* const nodes) {

    if (!specNode) {
        return;
    }

    int min = specNode->getMinOccurs();
    int max = specNode->getMaxOccurs();
    ContentSpecNode::NodeTypes nodeType = specNode->getType();
    ContentSpecNode* rightNode = specNode->getSecond();

    if (nodeType == ContentSpecNode::Leaf ||
        (nodeType & 0x0f) == ContentSpecNode::Any ||
        (nodeType & 0x0f) == ContentSpecNode::Any_NS ||
        (nodeType & 0x0f) == ContentSpecNode::Any_Other) {
        nodes->addElement(specNode);
    }
    else if (min !=1 || max != 1) {
        nodes->addElement(specNode);
    }
    else if (!rightNode) {
        gatherChildren(nodeType, specNode->getFirst(), nodes);
    }
    else if ((parentNodeType & 0x0f) == (nodeType & 0x0f)) {

        gatherChildren(nodeType, specNode->getFirst(), nodes);
        gatherChildren(nodeType, rightNode, nodes);
    }
    else {
        nodes->addElement(specNode);
    }
}

void
SchemaValidator::checkNSCompat(const ContentSpecNode* const derivedSpecNode,
                               const ContentSpecNode* const baseSpecNode,
                               const bool toCheckOccurence) {

    // check Occurrence ranges
    if (toCheckOccurence &&
        !isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_OccurRangeE,
                  derivedSpecNode->getElement()->getLocalPart(), fMemoryManager);
    }

    // check wildcard subset
    if (!wildcardEltAllowsNamespace(baseSpecNode, derivedSpecNode->getElement()->getURI())) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NSCompat1,
                  derivedSpecNode->getElement()->getLocalPart(), fMemoryManager);
    }
}

bool
SchemaValidator::wildcardEltAllowsNamespace(const ContentSpecNode* const baseSpecNode,
                                            const unsigned int derivedURI) {

    ContentSpecNode::NodeTypes nodeType = baseSpecNode->getType();

    if ((nodeType & 0x0f) == ContentSpecNode::Any) {
        return true;
    }

    unsigned int baseURI = baseSpecNode->getElement()->getURI();

    if ((nodeType & 0x0f) == ContentSpecNode::Any_NS) {
        if (derivedURI == baseURI) {
           return true;
        }
    }
    else { // must be ANY_OTHER
        if (derivedURI != baseURI && derivedURI != getScanner()->getEmptyNamespaceId()) {
            return true;
        }
    }

    return false;
}

void
SchemaValidator::checkNameAndTypeOK(SchemaGrammar* const currentGrammar,
                                    const ContentSpecNode* const derivedSpecNode,
                                    const int derivedScope,
                                    const ContentSpecNode* const baseSpecNode,
                                    const int baseScope,
                                    const ComplexTypeInfo* const baseInfo) {

    if (derivedSpecNode->getMaxOccurs() == 0)
        return;

    unsigned int derivedURI = derivedSpecNode->getElement()->getURI();

    // case of mixed complex types with attributes only
    if (derivedURI == XMLElementDecl::fgPCDataElemId) {
        return;
    }

    SchemaGrammar* aGrammar = currentGrammar;
    const XMLCh* schemaURI = fGrammarResolver->getStringPool()->getValueForId(derivedURI);

    if (derivedURI != getScanner()->getEmptyNamespaceId()) {
        aGrammar= (SchemaGrammar*) fGrammarResolver->getGrammar(schemaURI);
    }

    if (!aGrammar) { //something is wrong
        return;
    }
   
    const XMLCh* derivedName = derivedSpecNode->getElement()->getLocalPart();    

    SchemaElementDecl* derivedElemDecl = findElement(derivedScope, derivedURI, derivedName, aGrammar);

    if (!derivedElemDecl) {
        return;
    }

	const XMLCh* baseName = baseSpecNode->getElement()->getLocalPart();
	unsigned int baseURI = baseSpecNode->getElement()->getURI();
    bool subsGroup = false;

    if (!XMLString::equals(derivedName, baseName) || derivedURI != baseURI) {
        // Check if derived is substitutable for base.
        //
        SchemaElementDecl* e = derivedElemDecl->getSubstitutionGroupElem ();

        for (; e != 0; e = e->getSubstitutionGroupElem ()) {
            if (XMLString::equals(e->getBaseName (), baseName) && e->getURI () == baseURI) {
                break;
            }
        }

        if (e == 0) {
            ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_NameTypeOK1, fMemoryManager);
        }

        subsGroup = true;
    } 

    if (!isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_OccurRangeE, derivedName, fMemoryManager);
    }

    SchemaElementDecl* baseElemDecl =
        findElement(baseScope, baseURI, baseName, aGrammar, baseInfo);

    if (!baseElemDecl) {
        return;
    }

    int derivedFlags = derivedElemDecl->getMiscFlags();
    int baseFlags = baseElemDecl->getMiscFlags();

    if (((baseFlags & SchemaSymbols::XSD_NILLABLE) == 0) &&
		((derivedFlags & SchemaSymbols::XSD_NILLABLE) != 0)) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK2, derivedName, fMemoryManager);
    }

    const XMLCh* derivedDefVal = derivedElemDecl->getDefaultValue();
    const XMLCh* baseDefVal = baseElemDecl->getDefaultValue();

    if (baseDefVal && (baseFlags & SchemaSymbols::XSD_FIXED) != 0 &&
        ((derivedFlags & SchemaSymbols::XSD_FIXED) == 0 ||
         !XMLString::equals(derivedDefVal, baseDefVal))) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK3, derivedName, fMemoryManager);
    }

    int derivedBlockSet = derivedElemDecl->getBlockSet();
    int baseBlockSet = baseElemDecl->getBlockSet();

    if ((derivedBlockSet & baseBlockSet) != baseBlockSet) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK4, derivedName, fMemoryManager);
    }

    // check identity constraints
    checkICRestriction(derivedElemDecl, baseElemDecl, derivedName, baseName);

    // check that the derived element's type is derived from the base's.
    if (!subsGroup)
        checkTypesOK(derivedElemDecl, baseElemDecl, derivedName);
}

SchemaElementDecl*
SchemaValidator::findElement(const int scope, const unsigned int uriIndex,
                             const XMLCh* const name,
                             SchemaGrammar* const grammar,
                             const ComplexTypeInfo* const typeInfo) {

    // check for element at given scope first
    SchemaElementDecl* elemDecl = (SchemaElementDecl*) grammar->getElemDecl(uriIndex, name, 0, scope);

    // if not found, check at global scope
    if (!elemDecl) {

        elemDecl = (SchemaElementDecl*)
            grammar->getElemDecl(uriIndex, name, 0, Grammar::TOP_LEVEL_SCOPE);

        // if still not found, and base is specified, look it up there
        if (!elemDecl && typeInfo) {

            const ComplexTypeInfo* baseInfo = typeInfo;

            while (baseInfo) {

                elemDecl = (SchemaElementDecl*)
                    grammar->getElemDecl(uriIndex, name, 0, baseInfo->getScopeDefined());

                if (elemDecl) {
                   break;
                }

                baseInfo = baseInfo->getBaseComplexTypeInfo();
            }
        }
    }

    return elemDecl;
}

void
SchemaValidator::checkICRestriction(const SchemaElementDecl* const derivedElemDecl,
                                   const SchemaElementDecl* const baseElemDecl,
                                   const XMLCh* const derivedElemName,
                                   const XMLCh* const baseElemName) {

    // REVIST - need to get more clarification
    unsigned int derivedICCount = derivedElemDecl->getIdentityConstraintCount();
    unsigned int baseICCount = baseElemDecl->getIdentityConstraintCount();

    if (derivedICCount > baseICCount) {
        ThrowXMLwithMemMgr2(RuntimeException, XMLExcepts::PD_NameTypeOK6, derivedElemName, baseElemName, fMemoryManager);
    }

    for (unsigned int i=0; i < derivedICCount; i++) {

        bool found = false;
        IdentityConstraint* ic= derivedElemDecl->getIdentityConstraintAt(i);

        for (unsigned int j=0; j < baseICCount; j++) {
            if (*ic == *(baseElemDecl->getIdentityConstraintAt(j))) {

                found = true;
                break;
            }
        }

        if (!found) {
            ThrowXMLwithMemMgr2(RuntimeException, XMLExcepts::PD_NameTypeOK7, derivedElemName, baseElemName, fMemoryManager);
        }
    }
}

void
SchemaValidator::checkTypesOK(const SchemaElementDecl* const derivedElemDecl,
                              const SchemaElementDecl* const baseElemDecl,
                              const XMLCh* const derivedElemName) {

    SchemaElementDecl::ModelTypes baseType = baseElemDecl->getModelType();

    if (baseType == SchemaElementDecl::Any) {
        return;
    }

    ComplexTypeInfo* rInfo = derivedElemDecl->getComplexTypeInfo();
    ComplexTypeInfo* bInfo = baseElemDecl->getComplexTypeInfo();

    if (derivedElemDecl->getModelType() == SchemaElementDecl::Simple) {

        if (baseType != SchemaElementDecl::Simple) {
            ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK5, derivedElemName, fMemoryManager);
        }

        if (!rInfo) {

            DatatypeValidator* bDV = baseElemDecl->getDatatypeValidator();

            if (bInfo || bDV == 0 ||
				!bDV->isSubstitutableBy(derivedElemDecl->getDatatypeValidator())) {
                ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK5, derivedElemName, fMemoryManager);
            }

            return;
        }
    }

    if (rInfo == bInfo)
        return;

    for (; rInfo && rInfo != bInfo; rInfo = rInfo->getBaseComplexTypeInfo()) {
        if (rInfo->getDerivedBy() != SchemaSymbols::XSD_RESTRICTION) {

            rInfo = 0;
            break;
        }
    }

    if (!rInfo) {
        ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::PD_NameTypeOK5, derivedElemName, fMemoryManager);
    }
}

void
SchemaValidator::checkRecurseAsIfGroup(SchemaGrammar* const currentGrammar,
                                       ContentSpecNode* const derivedSpecNodeIn,
                                       const int derivedScope,
                                       const ContentSpecNode* const baseSpecNode,
                                       const int baseScope,
                                       ValueVectorOf<ContentSpecNode*>* const baseNodes,
                                       const ComplexTypeInfo* const baseInfo) {

    ContentSpecNode::NodeTypes baseType = baseSpecNode->getType();    
    bool toLax = false;

    //Treat the element as if it were in a group of the same variety as base
    ContentSpecNode derivedGroupNode(baseType, derivedSpecNodeIn, 0, false, true, fMemoryManager);
    const ContentSpecNode* const derivedSpecNode = &derivedGroupNode;
    
    if ((baseSpecNode->getType() & 0x0f) == ContentSpecNode::Choice) {
        toLax = true;
    }

    // Instead of calling this routine, inline it
    // checkRecurse(currentGrammar, &derivedGroupNode, derivedScope, &derivedNodes,
    //             baseSpecNode, baseScope, baseNodes, baseInfo, toLax);
 
    if (!isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_Recurse1, fMemoryManager);
    }

    // check for mapping of children
    XMLExcepts::Codes codeToThrow = XMLExcepts::NoError;   
    unsigned int count2= baseNodes->size();
    unsigned int current = 0;    

    {
        bool matched = false;

        for (unsigned int j = current; j < count2; j++) {

            ContentSpecNode* baseNode = baseNodes->elementAt(j);
            current++;

            bool bDoBreak=false;    // workaround for Borland bug with 'break' in 'catch'
            try {

                checkParticleDerivationOk(currentGrammar, derivedSpecNodeIn,
                                          derivedScope, baseNode, baseScope, baseInfo);
                matched = true;
                break;
            }
            catch(const XMLException&) {
                if (!toLax && baseNode->getMinTotalRange()) {
                    bDoBreak=true;
                }
            }
            if(bDoBreak)
                break;
        }

        // did not find a match
        if (!matched) {
            codeToThrow = XMLExcepts::PD_Recurse2;            
        }
    }

    // Now, see if there are some elements in the base we didn't match up
    // in case of Sequence or All
    if (!toLax && codeToThrow == XMLExcepts::NoError &&
        (true || (baseType & 0x0f) == ContentSpecNode::All || 
         derivedSpecNodeIn->getElement()->getURI() != XMLElementDecl::fgPCDataElemId)) {
        for (unsigned int j = current; j < count2; j++) {
            if (baseNodes->elementAt(j)->getMinTotalRange() * baseSpecNode->getMinOccurs()) { //!emptiable
                codeToThrow =  XMLExcepts::PD_Recurse2;                
                break;
            }
        }
    }

    if (codeToThrow != XMLExcepts::NoError) {
        ThrowXMLwithMemMgr(RuntimeException, codeToThrow, fMemoryManager);
    }
}

void
SchemaValidator::checkRecurse(SchemaGrammar* const currentGrammar,
                              const ContentSpecNode* const derivedSpecNode,
                              const int derivedScope,
                              ValueVectorOf<ContentSpecNode*>* const derivedNodes,
                              const ContentSpecNode* const baseSpecNode,
                              const int baseScope,
                              ValueVectorOf<ContentSpecNode*>* const baseNodes,
                              const ComplexTypeInfo* const baseInfo,
                              const bool toLax) {

    if (!isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_Recurse1, fMemoryManager);
    }

    // check for mapping of children
    XMLExcepts::Codes codeToThrow = XMLExcepts::NoError;
    unsigned int count1= derivedNodes->size();
    unsigned int count2= baseNodes->size();
    unsigned int current = 0;    

    for (unsigned int i=0; i<count1; i++) {

        bool matched = false;

        for (unsigned int j = current; j < count2; j++) {

            ContentSpecNode* baseNode = baseNodes->elementAt(j);
            current++;

            bool bDoBreak=false;    // workaround for Borland bug with 'break' in 'catch'
            try {

                checkParticleDerivationOk(currentGrammar, derivedNodes->elementAt(i),
                                          derivedScope, baseNode, baseScope, baseInfo);
                matched = true;
                break;
            }
            catch(const XMLException&) {
                if (!toLax && baseNode->getMinTotalRange()) {
                    bDoBreak=true;
                }
            }
            if(bDoBreak)
                break;
        }

        // did not find a match
        if (!matched) {

            codeToThrow = XMLExcepts::PD_Recurse2;
            break;
        }
    }

    // Now, see if there are some elements in the base we didn't match up
    // in case of Sequence or All
    if (!toLax && codeToThrow == XMLExcepts::NoError) {
        for (unsigned int j = current; j < count2; j++) {
            if (baseNodes->elementAt(j)->getMinTotalRange()) { //!emptiable                
                codeToThrow =  XMLExcepts::PD_Recurse2;                
                break;
            }
        }
    }

    if (codeToThrow != XMLExcepts::NoError) {
        ThrowXMLwithMemMgr(RuntimeException, codeToThrow, fMemoryManager);
    }
}

void SchemaValidator::checkNSSubset(const ContentSpecNode* const derivedSpecNode,
                                    const ContentSpecNode* const baseSpecNode) {

    // check Occurrence ranges
    if (!isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_NSSubset1, fMemoryManager);
    }

    if (!isWildCardEltSubset(derivedSpecNode, baseSpecNode)) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_NSSubset2, fMemoryManager);
    }
}

bool
SchemaValidator::isWildCardEltSubset(const ContentSpecNode* const derivedSpecNode,
                                     const ContentSpecNode* const baseSpecNode) {

    ContentSpecNode::NodeTypes baseType = baseSpecNode->getType();

    if ((baseType & 0x0f) == ContentSpecNode::Any) {
        return true;
    }

    ContentSpecNode::NodeTypes derivedType = derivedSpecNode->getType();
    unsigned int baseURI = baseSpecNode->getElement()->getURI();
    unsigned int derivedURI = derivedSpecNode->getElement()->getURI();

    if (((derivedType & 0x0f) == ContentSpecNode::Any_Other) &&
        ((baseType & 0x0f) == ContentSpecNode::Any_Other) &&
        baseURI == derivedURI) {
        return true;
    }

    if ((derivedType & 0x0f) == ContentSpecNode::Any_NS) {

        if (((baseType & 0x0f) == ContentSpecNode::Any_NS) &&
            baseURI == derivedURI) {
            return true;
        }

        if (((baseType & 0x0f) == ContentSpecNode::Any_Other) &&
            baseURI != derivedURI) {
            return true;
        }
    }

    return false;
}

void
SchemaValidator::checkNSRecurseCheckCardinality(SchemaGrammar* const currentGrammar,
                                                const ContentSpecNode* const derivedSpecNode,
                                                ValueVectorOf<ContentSpecNode*>* const derivedNodes,
                                                const int derivedScope,
                                                ContentSpecNode* const baseSpecNode,
                                                const bool toCheckOccurence) {

    // Implement total range check
    int derivedMin = derivedSpecNode->getMinTotalRange();
    int derivedMax = derivedSpecNode->getMaxTotalRange();

    // check Occurrence ranges
    if (toCheckOccurence &&
        !isOccurrenceRangeOK(derivedMin, derivedMax, baseSpecNode->getMinOccurs(),
                              baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_NSRecurseCheckCardinality1, fMemoryManager);
    }

    // Check that each member of the group is a valid restriction of the wildcard
    unsigned int nodesCount = derivedNodes->size();

    for (unsigned int i = 0; i < nodesCount; i++) {
        checkParticleDerivationOk(currentGrammar, derivedNodes->elementAt(i), derivedScope, baseSpecNode, -1, 0, false);
    }
}

void
SchemaValidator::checkRecurseUnordered(SchemaGrammar* const currentGrammar,
                                       const ContentSpecNode* const derivedSpecNode,
                                       ValueVectorOf<ContentSpecNode*>* const derivedNodes,
                                       const int derivedScope,
                                       ContentSpecNode* const baseSpecNode,
                                       ValueVectorOf<ContentSpecNode*>* const baseNodes,
                                       const int baseScope,
                                       const ComplexTypeInfo* const baseInfo) {

    // check Occurrence ranges
    if (!isOccurrenceRangeOK(derivedSpecNode->getMinOccurs(), derivedSpecNode->getMaxOccurs(),
                             baseSpecNode->getMinOccurs(), baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_Recurse1, fMemoryManager);
    }

    XMLExcepts::Codes  codeToThrow = XMLExcepts::NoError;
    unsigned int       derivedCount= derivedNodes->size();
    unsigned int       baseCount = baseNodes->size();
    bool*              foundIt = (bool*) fMemoryManager->allocate
    (
        baseCount * sizeof(bool)
    );//new bool[baseCount];
    ArrayJanitor<bool> janFoundIt(foundIt, fMemoryManager);

    for (unsigned k=0; k < baseCount; k++) {
        foundIt[k] = false;
    }

    // check for mapping of children
    for (unsigned int i = 0; i < derivedCount; i++) {

        ContentSpecNode* derivedNode = derivedNodes->elementAt(i);
        bool matched = false;

        for (unsigned int j = 0; j < baseCount; j++) {

            try {

                checkParticleDerivationOk(currentGrammar, derivedNode, derivedScope,
                                          baseNodes->elementAt(j), baseScope, baseInfo);

                if (foundIt[j]) {
                    break;
                }

                foundIt[j] = true;
                matched = true;
                break;
            }
            catch (const XMLException&) {
            }
        }

        // didn't find a match.
        if (!matched) {

	        codeToThrow = XMLExcepts::PD_RecurseUnordered;
            break;
        }
    }

    // For all unmapped particles in base, check to see it it's emptiable or not
    if (codeToThrow == XMLExcepts::NoError) {
        for (unsigned int j=0; j < baseCount; j++) {
            if (!foundIt[j] && baseNodes->elementAt(j)->getMinTotalRange()) {

	            codeToThrow = XMLExcepts::PD_RecurseUnordered;
                break;
            }
        }
    }

    if (codeToThrow != XMLExcepts::NoError) {
        ThrowXMLwithMemMgr(RuntimeException, codeToThrow, fMemoryManager);
    }
}

void
SchemaValidator::checkMapAndSum(SchemaGrammar* const currentGrammar,
                                const ContentSpecNode* const derivedSpecNode,
                                ValueVectorOf<ContentSpecNode*>* const derivedNodes,
                                const int derivedScope,
                                ContentSpecNode* const baseSpecNode,
                                ValueVectorOf<ContentSpecNode*>* const baseNodes,
                                const int baseScope,
                                const ComplexTypeInfo* const baseInfo) {

    // check Occurrence ranges
    int derivedCount = derivedNodes->size();
    int baseCount = baseNodes->size();
    int derivedMin = derivedSpecNode->getMinOccurs() * derivedCount;
    int derivedMax = derivedSpecNode->getMaxOccurs();

    if (derivedMax != SchemaSymbols::XSD_UNBOUNDED) {
        derivedMax *= derivedCount;
    }

    if (!isOccurrenceRangeOK(derivedMin, derivedMax, baseSpecNode->getMinOccurs(),
                             baseSpecNode->getMaxOccurs())) {
        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_Recurse1, fMemoryManager);
    }

    // check for mapping of children
    for (int i = 0; i < derivedCount; i++) {

        ContentSpecNode* derivedNode = derivedNodes->elementAt(i);
        bool matched = false;

        for (int j = 0; j < baseCount && !matched; j++) {

            try {

                checkParticleDerivationOk(currentGrammar, derivedNode, derivedScope,
                                          baseNodes->elementAt(j), baseScope, baseInfo);
                matched = true;
            }
            catch (const XMLException&) {
            }
        }

        // didn't find a match.
        if (!matched) {
	        ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::PD_MapAndSum, fMemoryManager);
        }
    }

}

XERCES_CPP_NAMESPACE_END
