blob: 4de570de9523910a0c1ba0ba793de0a1e42ebf71 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ---------------------------------------------------------------------------
// 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