blob: 8c8233a5eb11282f74ec06e59c006297060e0db6 [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: MixedContentModel.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <string.h>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/framework/XMLElementDecl.hpp>
#include <xercesc/validators/common/ContentSpecNode.hpp>
#include <xercesc/validators/common/MixedContentModel.hpp>
#include <xercesc/validators/common/CMStateSet.hpp>
#include <xercesc/validators/common/Grammar.hpp>
#include <xercesc/validators/schema/SubstitutionGroupComparator.hpp>
XERCES_CPP_NAMESPACE_BEGIN
// ---------------------------------------------------------------------------
// MixedContentModel: Constructors and Destructor
// ---------------------------------------------------------------------------
MixedContentModel::MixedContentModel(const bool dtd
, ContentSpecNode* const parentContentSpec
, const bool ordered
, MemoryManager* const manager) :
fCount(0)
, fChildren(0)
, fChildTypes(0)
, fOrdered(ordered)
, fDTD(dtd)
, fMemoryManager(manager)
{
//
// 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<ContentSpecNode::NodeTypes> childTypes(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
buildChildList(curNode, children, childTypes);
//
// 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];
fChildTypes = (ContentSpecNode::NodeTypes*) fMemoryManager->allocate
(
fCount * sizeof(ContentSpecNode::NodeTypes)
); //new ContentSpecNode::NodeTypes[fCount];
for (unsigned int index = 0; index < fCount; index++) {
fChildren[index] = new (fMemoryManager) QName(*children.elementAt(index));
fChildTypes[index] = childTypes.elementAt(index);
}
}
MixedContentModel::~MixedContentModel()
{
for (unsigned int index = 0; index < fCount; index++) {
delete fChildren[index];
}
fMemoryManager->deallocate(fChildren); //delete [] fChildren;
fMemoryManager->deallocate(fChildTypes); //delete [] fChildTypes;
}
// ---------------------------------------------------------------------------
// MixedContentModel: Getter methods
// ---------------------------------------------------------------------------
bool MixedContentModel::hasDups() const
{
// Can't have dups if only one child
if (fCount == 1)
return false;
for (unsigned int index = 0; index < fCount; index++)
{
const QName* curVal = fChildren[index];
for (unsigned int iIndex = 0; iIndex < fCount; iIndex++)
{
if (iIndex == index)
continue;
if (fDTD) {
if (XMLString::equals(curVal->getRawName(), fChildren[iIndex]->getRawName())) {
return true;
}
}
else {
if ((curVal->getURI() == fChildren[iIndex]->getURI()) &&
(XMLString::equals(curVal->getLocalPart(), fChildren[iIndex]->getLocalPart()))) {
return true;
}
}
}
}
return false;
}
// ---------------------------------------------------------------------------
// MixedContentModel: 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
MixedContentModel::validateContent( QName** const children
, const unsigned int childCount
, const unsigned int) const
{
// must match order
if (fOrdered) {
unsigned int inIndex = 0;
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
const QName* curChild = children[outIndex];
// If its PCDATA, then we just accept that
if (curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
ContentSpecNode::NodeTypes type = fChildTypes[inIndex];
const QName* inChild = fChildren[inIndex];
if (type == ContentSpecNode::Leaf) {
if (fDTD) {
if (!XMLString::equals(inChild->getRawName(), curChild->getRawName())) {
return outIndex;
}
}
else {
if ((inChild->getURI() != curChild->getURI()) ||
(!XMLString::equals(inChild->getLocalPart(), curChild->getLocalPart()))) {
return outIndex;
}
}
}
else if (type == ContentSpecNode::Any) {
}
else if (type == ContentSpecNode::Any_NS) {
if (inChild->getURI() != curChild->getURI())
return outIndex;
}
else if (type == ContentSpecNode::Any_Other) {
if (inChild->getURI() == curChild->getURI())
return outIndex;
}
// advance index
inIndex++;
}
}
// can appear in any order
else {
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
const QName* curChild = children[outIndex];
// If its PCDATA, then we just accept that
if (curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
// And try to find it in our list
unsigned int inIndex = 0;
for (; inIndex < fCount; inIndex++)
{
ContentSpecNode::NodeTypes type = fChildTypes[inIndex];
const QName* inChild = fChildren[inIndex];
if (type == ContentSpecNode::Leaf) {
if (fDTD) {
if (XMLString::equals(inChild->getRawName(), curChild->getRawName())) {
break;
}
}
else {
if ((inChild->getURI() == curChild->getURI()) &&
(XMLString::equals(inChild->getLocalPart(), curChild->getLocalPart()))) {
break;
}
}
}
else if (type == ContentSpecNode::Any) {
break;
}
else if (type == ContentSpecNode::Any_NS) {
if (inChild->getURI() == curChild->getURI())
break;
}
else if (type == ContentSpecNode::Any_Other) {
if (inChild->getURI() != curChild->getURI())
break;
}
// REVISIT: What about checking for multiple ANY matches?
// The content model ambiguity *could* be checked
// by the caller before constructing the mixed
// content model.
}
// We did not find this one, so the validation failed
if (inIndex == fCount)
return outIndex;
}
}
// Everything seems to be in order, so return success
// success
return -1;
}
int MixedContentModel::validateContentSpecial(QName** const children
, const unsigned int childCount
, const unsigned int
, GrammarResolver* const pGrammarResolver
, XMLStringPool* const pStringPool) const
{
SubstitutionGroupComparator comparator(pGrammarResolver, pStringPool);
// must match order
if (fOrdered) {
unsigned int inIndex = 0;
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
QName* curChild = children[outIndex];
// If its PCDATA, then we just accept that
if (curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
ContentSpecNode::NodeTypes type = fChildTypes[inIndex];
QName* inChild = fChildren[inIndex];
if (type == ContentSpecNode::Leaf) {
if ( !comparator.isEquivalentTo(curChild, inChild))
return outIndex;
}
else if (type == ContentSpecNode::Any) {
}
else if (type == ContentSpecNode::Any_NS) {
if (inChild->getURI() != curChild->getURI())
return outIndex;
}
else if (type == ContentSpecNode::Any_Other) {
if (inChild->getURI() == curChild->getURI())
return outIndex;
}
// advance index
inIndex++;
}
}
// can appear in any order
else {
for (unsigned int outIndex = 0; outIndex < childCount; outIndex++) {
// Get the current child out of the source index
QName* curChild = children[outIndex];
// If its PCDATA, then we just accept that
if (curChild->getURI() == XMLElementDecl::fgPCDataElemId)
continue;
// And try to find it in our list
unsigned int inIndex = 0;
for (; inIndex < fCount; inIndex++)
{
ContentSpecNode::NodeTypes type = fChildTypes[inIndex];
QName* inChild = fChildren[inIndex];
if (type == ContentSpecNode::Leaf) {
if ( comparator.isEquivalentTo(curChild, inChild))
break;
}
else if (type == ContentSpecNode::Any) {
break;
}
else if (type == ContentSpecNode::Any_NS) {
if (inChild->getURI() == curChild->getURI())
break;
}
else if (type == ContentSpecNode::Any_Other) {
if (inChild->getURI() != curChild->getURI())
break;
}
// REVISIT: What about checking for multiple ANY matches?
// The content model ambiguity *could* be checked
// by the caller before constructing the mixed
// content model.
}
// We did not find this one, so the validation failed
if (inIndex == fCount)
return outIndex;
}
}
// Everything seems to be in order, so return success
// success
return -1;
}
// ---------------------------------------------------------------------------
// MixedContentModel: Private helper methods
// ---------------------------------------------------------------------------
void
MixedContentModel::buildChildList( ContentSpecNode* const curNode
, ValueVectorOf<QName*>& toFill
, ValueVectorOf<ContentSpecNode::NodeTypes>& toType)
{
// Get the type of spec node our current node is
const ContentSpecNode::NodeTypes curType = curNode->getType();
// If its a leaf, then store its id in the target list
if ((curType == ContentSpecNode::Leaf) ||
(curType == ContentSpecNode::Any) ||
(curType == ContentSpecNode::Any_Other) ||
(curType == ContentSpecNode::Any_NS) )
{
toFill.addElement(curNode->getElement());
toType.addElement(curType);
return;
}
// Get both the child node pointers
ContentSpecNode* leftNode = curNode->getFirst();
ContentSpecNode* rightNode = curNode->getSecond();
// And recurse according to the type of node
if (((curType & 0x0f) == ContentSpecNode::Choice)
|| ((curType & 0x0f) == ContentSpecNode::Sequence))
{
// Recurse on the left and right nodes
buildChildList(leftNode, toFill, toType);
// The last node of a choice or sequence has a null right
if (rightNode)
buildChildList(rightNode, toFill, toType);
}
else if ((curType == ContentSpecNode::OneOrMore)
|| (curType == ContentSpecNode::ZeroOrOne)
|| (curType == ContentSpecNode::ZeroOrMore))
{
// Just do the left node on this one
buildChildList(leftNode, toFill, toType);
}
}
XERCES_CPP_NAMESPACE_END