blob: 8c8a0e226c18c31c3d030f5a572d2fd1d8a89479 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: AllContentModel.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/framework/XMLElementDecl.hpp>
#include <xercesc/framework/XMLValidator.hpp>
#include <xercesc/validators/common/ContentSpecNode.hpp>
#include <xercesc/validators/common/AllContentModel.hpp>
#include <xercesc/validators/schema/SubstitutionGroupComparator.hpp>
#include <xercesc/validators/schema/XercesElementWildcard.hpp>
XERCES_CPP_NAMESPACE_BEGIN
// ---------------------------------------------------------------------------
// AllContentModel: Constructors and Destructor
// ---------------------------------------------------------------------------
AllContentModel::AllContentModel( ContentSpecNode* const parentContentSpec
, const bool isMixed
, MemoryManager* const manager) :
fMemoryManager(manager)
, fCount(0)
, fChildren(0)
, fChildOptional(0)
, fNumRequired(0)
, fIsMixed(isMixed)
, fHasOptionalContent(false)
{
//
// Create a vector of unsigned ints that will be filled in with the
// ids of the child nodes. It will be expanded as needed but we give
// it an initial capacity of 64 which should be more than enough for
// 99% of the scenarios.
//
ValueVectorOf<QName*> children(64, fMemoryManager);
ValueVectorOf<bool> childOptional(64, fMemoryManager);
//
// Get the parent element's content spec. This is the head of the tree
// of nodes that describes the content model. We will iterate this
// tree.
//
ContentSpecNode* curNode = parentContentSpec;
if (!curNode)
ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_NoParentCSN, fMemoryManager);
// And now call the private recursive method that iterates the tree
if (curNode->getType() == ContentSpecNode::All
&& curNode->getMinOccurs() == 0) {
fHasOptionalContent = true;
}
buildChildList(curNode, children, childOptional);
//
// And now we know how many elements we need in our member list. So
// fill them in.
//
fCount = children.size();
fChildren = (QName**) fMemoryManager->allocate(fCount * sizeof(QName*)); //new QName*[fCount];
fChildOptional = (bool*) fMemoryManager->allocate(fCount * sizeof(bool)); //new bool[fCount];
for (unsigned int index = 0; index < fCount; index++) {
fChildren[index] = new (fMemoryManager) QName(*(children.elementAt(index)));
fChildOptional[index] = childOptional.elementAt(index);
}
}
AllContentModel::~AllContentModel()
{
for (unsigned int index = 0; index < fCount; index++)
delete fChildren[index];
fMemoryManager->deallocate(fChildren); //delete [] fChildren;
fMemoryManager->deallocate(fChildOptional); //delete [] fChildOptional;
}
// ---------------------------------------------------------------------------
// AllContentModel: Implementation of the ContentModel virtual interface
// ---------------------------------------------------------------------------
//
//Under the XML Schema mixed model,
//the order and number of child elements appearing in an instance
//must agree with
//the order and number of child elements specified in the model.
//
int
AllContentModel::validateContent( QName** const children
, const unsigned int childCount
, const unsigned int) const
{
// If <all> had minOccurs of zero and there are
// no children to validate, trivially validate
if (childCount == 0 && (fHasOptionalContent || !fNumRequired))
return -1;
// keep track of the required element seen
unsigned int numRequiredSeen = 0;
if(childCount > 0)
{
MemoryManager* const localMemoryManager = children[0]->getMemoryManager();
// Check for duplicate element
bool* elementSeen = (bool*) localMemoryManager->allocate(fCount*sizeof(bool)); //new bool[fCount];
const ArrayJanitor<bool> jan(elementSeen, localMemoryManager);
// initialize the array
for (unsigned int i = 0; i < fCount; i++)
elementSeen[i] = false;
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
const QName* curChild = children[outIndex];
// If it's PCDATA, then we just accept that
if (fIsMixed && curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
// And try to find it in our list
unsigned int inIndex = 0;
for (; inIndex < fCount; inIndex++)
{
const QName* inChild = fChildren[inIndex];
if ((inChild->getURI() == curChild->getURI()) &&
(XMLString::equals(inChild->getLocalPart(), curChild->getLocalPart()))) {
// found it
// If this element was seen already, indicate an error was
// found at the duplicate index.
if (elementSeen[inIndex]) {
return outIndex;
}
else
elementSeen[inIndex] = true;
if (!fChildOptional[inIndex])
numRequiredSeen++;
break;
}
}
// We did not find this one, so the validation failed
if (inIndex == fCount) {
return outIndex;
}
}
}
// Were all the required elements of the <all> encountered?
if (numRequiredSeen != fNumRequired) {
return childCount;
}
// Everything seems to be ok, so return success
return -1;
}
int AllContentModel::validateContentSpecial(QName** const children
, const unsigned int childCount
, const unsigned int
, GrammarResolver* const pGrammarResolver
, XMLStringPool* const pStringPool) const
{
// If <all> had minOccurs of zero and there are
// no children to validate, trivially validate
if (childCount == 0 && (fHasOptionalContent || !fNumRequired))
return -1;
// keep track of the required element seen
unsigned int numRequiredSeen = 0;
if(childCount > 0)
{
SubstitutionGroupComparator comparator(pGrammarResolver, pStringPool);
MemoryManager* const localMemoryManager = children[0]->getMemoryManager();
// Check for duplicate element
bool* elementSeen = (bool*) localMemoryManager->allocate(fCount*sizeof(bool)); //new bool[fCount];
const ArrayJanitor<bool> jan(elementSeen, localMemoryManager);
// initialize the array
for (unsigned int i = 0; i < fCount; i++)
elementSeen[i] = false;
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
QName* const curChild = children[outIndex];
// If it's PCDATA, then we just accept that
if (fIsMixed && curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
// And try to find it in our list
unsigned int inIndex = 0;
for (; inIndex < fCount; inIndex++)
{
QName* const inChild = fChildren[inIndex];
if ( comparator.isEquivalentTo(curChild, inChild)) {
// match
// If this element was seen already, indicate an error was
// found at the duplicate index.
if (elementSeen[inIndex]) {
return outIndex;
}
else
elementSeen[inIndex] = true;
if (!fChildOptional[inIndex])
numRequiredSeen++;
break;
}
}
// We did not find this one, so the validation failed
if (inIndex == fCount) {
return outIndex;
}
}
}
// Were all the required elements of the <all> encountered?
if (numRequiredSeen != fNumRequired) {
return childCount;
}
// Everything seems to be ok, so return success
return -1;
}
void AllContentModel::checkUniqueParticleAttribution
(
SchemaGrammar* const pGrammar
, GrammarResolver* const pGrammarResolver
, XMLStringPool* const pStringPool
, XMLValidator* const pValidator
, unsigned int* const pContentSpecOrgURI
, const XMLCh* pComplexTypeName /*= 0*/
)
{
SubstitutionGroupComparator comparator(pGrammarResolver, pStringPool);
unsigned int i, j;
// rename back
for (i = 0; i < fCount; i++) {
unsigned int orgURIIndex = fChildren[i]->getURI();
fChildren[i]->setURI(pContentSpecOrgURI[orgURIIndex]);
}
// check whether there is conflict between any two leaves
for (i = 0; i < fCount; i++) {
for (j = i+1; j < fCount; j++) {
// If this is text in a Schema mixed content model, skip it.
if ( fIsMixed &&
(( fChildren[i]->getURI() == XMLElementDecl::fgPCDataElemId) ||
( fChildren[j]->getURI() == XMLElementDecl::fgPCDataElemId)))
continue;
if (XercesElementWildcard::conflict(pGrammar,
ContentSpecNode::Leaf,
fChildren[i],
ContentSpecNode::Leaf,
fChildren[j],
&comparator)) {
pValidator->emitError(XMLValid::UniqueParticleAttributionFail,
pComplexTypeName,
fChildren[i]->getRawName(),
fChildren[j]->getRawName());
}
}
}
}
// ---------------------------------------------------------------------------
// AllContentModel: Private helper methods
// ---------------------------------------------------------------------------
void
AllContentModel::buildChildList(ContentSpecNode* const curNode
, ValueVectorOf<QName*>& toFill
, ValueVectorOf<bool>& toOptional)
{
// Get the type of spec node our current node is
const ContentSpecNode::NodeTypes curType = curNode->getType();
if (curType == ContentSpecNode::All)
{
// Get both the child node pointers
ContentSpecNode* leftNode = curNode->getFirst();
ContentSpecNode* rightNode = curNode->getSecond();
// Recurse on the left and right nodes
buildChildList(leftNode, toFill, toOptional);
buildChildList(rightNode, toFill, toOptional);
}
else if (curType == ContentSpecNode::Leaf)
{
// At leaf, add the element to list of elements permitted in the all
toFill.addElement(curNode->getElement());
toOptional.addElement(false);
fNumRequired++;
}
else if (curType == ContentSpecNode::ZeroOrOne)
{
// At ZERO_OR_ONE node, subtree must be an element
// that was specified with minOccurs=0, maxOccurs=1
ContentSpecNode* leftNode = curNode->getFirst();
if (leftNode->getType() != ContentSpecNode::Leaf)
ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_UnknownCMSpecType, fMemoryManager);
toFill.addElement(leftNode->getElement());
toOptional.addElement(true);
}
else
ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::CM_UnknownCMSpecType, fMemoryManager);
}
XERCES_CPP_NAMESPACE_END