blob: 009f19142f84fadce0ace10a2ee120f7e7138929 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache\@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation, and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com . For more information
* on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* $Log$
* Revision 1.2 2002/02/06 22:29:24 knoaman
* Remove unnecessary data/methods.
*
* Revision 1.1.1.1 2002/02/01 22:22:45 peiyongz
* sane_include
*
* Revision 1.27 2002/01/15 19:09:16 knoaman
* Fix for bug 5807.
*
* Revision 1.26 2002/01/02 15:20:22 tng
* Schema Fix: should not store a temp value as the key in the element pool and the attribute pool.
*
* Revision 1.25 2001/12/17 21:38:59 tng
* Fix dangling pointer.
*
* Revision 1.24 2001/11/21 14:30:13 knoaman
* Fix for UPA checking.
*
* Revision 1.23 2001/11/15 17:10:19 knoaman
* Particle derivation checking support.
*
* Revision 1.22 2001/11/07 21:50:28 tng
* Fix comment log that lead to error.
*
* Revision 1.21 2001/11/07 21:12:15 tng
* Performance: Create QName in ContentSpecNode only if it is a leaf/Any/PCDataNode.
*
* Revision 1.20 2001/10/12 18:08:57 tng
* make sure the second node exists before calling getType
*
* Revision 1.19 2001/10/04 15:08:56 knoaman
* Add support for circular import.
*
* Revision 1.18 2001/09/05 20:49:11 knoaman
* Fix for complexTypes with mixed content model.
*
* Revision 1.17 2001/08/29 21:27:07 knoaman
* no message
*
* Revision 1.16 2001/08/28 20:43:52 knoaman
* Fix for content spec node adoption.
*
* Revision 1.15 2001/08/27 23:04:02 knoaman
* Handle deletion of spec node tree created during UPA checking.
*
* Revision 1.14 2001/08/27 20:48:45 knoaman
* Make the UPA rename before the content model expansion.
*
* Revision 1.13 2001/08/27 20:14:42 knoaman
* Validate attributes in <all>, <redefine>, <group> and <attributeGroup> declarations.
* Misc. fixes.
*
* Revision 1.12 2001/08/24 12:48:48 tng
* Schema: AllContentModel
*
* Revision 1.11 2001/08/23 11:54:26 tng
* Add newline at the end and various typo fixes.
*
* Revision 1.10 2001/08/21 18:14:55 knoaman
* Defer creation of spec node.
*
* Revision 1.9 2001/08/21 16:06:11 tng
* Schema: Unique Particle Attribution Constraint Checking.
*
* Revision 1.8 2001/08/09 15:23:16 knoaman
* add support for <anyAttribute> declaration.
*
* Revision 1.7 2001/07/24 18:33:46 knoaman
* Added support for <group> + extra constraint checking for complexType
*
* Revision 1.6 2001/06/05 13:59:53 knoaman
* Fixes to include and import.
*
* Revision 1.5 2001/05/11 13:27:32 tng
* Copyright update.
*
* Revision 1.4 2001/05/10 17:49:40 tng
* Schema: SchemaValidator fixes
*
* Revision 1.3 2001/05/10 16:33:12 knoaman
* Traverse Schema Part III + error messages.
*
* Revision 1.2 2001/05/03 20:34:41 tng
* Schema: SchemaValidator update
*
* Revision 1.1 2001/04/19 17:43:14 knoaman
* More schema implementation classes.
*
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/framework/XMLBuffer.hpp>
#include <xercesc/validators/schema/ComplexTypeInfo.hpp>
#include <xercesc/validators/schema/SchemaAttDefList.hpp>
#include <xercesc/validators/common/AllContentModel.hpp>
#include <xercesc/validators/common/ContentSpecNode.hpp>
#include <xercesc/validators/common/DFAContentModel.hpp>
#include <xercesc/validators/common/MixedContentModel.hpp>
#include <xercesc/validators/common/SimpleContentModel.hpp>
// ---------------------------------------------------------------------------
// ComplexTypeInfo: Constructors and Destructor
// ---------------------------------------------------------------------------
ComplexTypeInfo::ComplexTypeInfo()
: fAbstract(false)
, fAdoptContentSpec(true)
, fAttWithTypeId(false)
, fPreprocessed(false)
, fDerivedBy(0)
, fBlockSet(0)
, fFinalSet(0)
, fScopeDefined(Grammar::TOP_LEVEL_SCOPE)
, fElementId(XMLElementDecl::fgInvalidElemId)
, fContentType(SchemaElementDecl::Empty)
, fTypeName(0)
, fBaseDatatypeValidator(0)
, fDatatypeValidator(0)
, fBaseComplexTypeInfo(0)
, fContentSpec(0)
, fAttWildCard(0)
, fAttDefs(0)
, fAttList(0)
, fElements(0)
, fContentModel(0)
, fFormattedModel(0)
, fContentSpecOrgURI(0)
, fUniqueURI(0)
, fContentSpecOrgURISize(16)
, fSpecNodesToDelete(0)
{
}
ComplexTypeInfo::~ComplexTypeInfo()
{
delete [] fTypeName;
if (fAdoptContentSpec) {
delete fContentSpec;
}
delete fAttWildCard;
delete fAttDefs;
delete fAttList;
delete fElements;
delete fSpecNodesToDelete;
delete fContentModel;
delete [] fFormattedModel;
delete [] fContentSpecOrgURI;
}
// ---------------------------------------------------------------------------
// ComplexTypeInfo: Setter methods
// ---------------------------------------------------------------------------
void ComplexTypeInfo::addAttDef(SchemaAttDef* const toAdd) {
// Fault in the att list if required
if (!fAttDefs)
faultInAttDefList();
// Tell this guy the element id of its parent (us)
toAdd->setElemId(getElementId());
fAttDefs->put((void*)(toAdd->getAttName()->getLocalPart()),
toAdd->getAttName()->getURI(), toAdd);
}
void ComplexTypeInfo::setContentSpec(ContentSpecNode* const toAdopt) {
if (fContentSpec && fAdoptContentSpec) {
delete fContentSpec;
}
fContentSpec = toAdopt;
}
// ---------------------------------------------------------------------------
// ComplexTypeInfo: Getter methods
// ---------------------------------------------------------------------------
XMLAttDefList& ComplexTypeInfo::getAttDefList() const
{
if (!fAttList)
{
// If the att def list is not made yet, then fault it in too
if (!fAttDefs)
faultInAttDefList();
((ComplexTypeInfo*)this)->fAttList = new SchemaAttDefList(fAttDefs);
}
// Reset it before we return it
fAttList->Reset();
return *fAttList;
}
const XMLCh*
ComplexTypeInfo::getFormattedContentModel() const
{
//
// If its not already built, then call the protected virtual method
// to allow the derived class to build it (since only it knows.)
// Otherwise, just return the previously formatted methods.
//
// Since we are faulting this in, within a const getter, we have to
// cast off the const-ness.
//
if (!fFormattedModel)
((ComplexTypeInfo*)this)->fFormattedModel = formatContentModel();
return fFormattedModel;
}
// ---------------------------------------------------------------------------
// ComplexTypeInfo: Helper methods
// ---------------------------------------------------------------------------
XMLAttDef* ComplexTypeInfo::findAttr(const XMLCh* const qName
, const unsigned int uriId
, const XMLCh* const baseName
, const XMLCh* const prefix
, const XMLElementDecl::LookupOpts options
, bool& wasAdded) const
{
SchemaAttDef* retVal = 0;
// If no att list faulted in yet, then it cannot exist
if (fAttDefs)
retVal = fAttDefs->get(baseName, uriId);
// Fault it in if not found and ask to add it
if (!retVal && (options == XMLElementDecl::AddIfNotFound))
{
// Fault in the list itself if not already
if (!fAttDefs)
faultInAttDefList();
// And add a default attribute for this name
retVal = new SchemaAttDef(prefix, baseName, uriId);
retVal->setElemId(getElementId());
fAttDefs->put((void*)retVal->getAttName()->getLocalPart(), uriId, retVal);
wasAdded = true;
}
else
{
wasAdded = false;
}
return retVal;
}
bool ComplexTypeInfo::resetDefs() {
// If the collection hasn't been faulted in, then no att defs
if (!fAttDefs)
return false;
//
// Ok, run through them and clear the 'provided' flag on each of them.
// This lets the scanner use them to track which has been provided and
// which have not.
//
RefHash2KeysTableOfEnumerator<SchemaAttDef> enumDefs(fAttDefs);
while (enumDefs.hasMoreElements())
enumDefs.nextElement().setProvided(false);
return true;
}
void ComplexTypeInfo::checkUniqueParticleAttribution (SchemaGrammar* const pGrammar,
GrammarResolver* const pGrammarResolver,
XMLStringPool* const pStringPool,
XMLValidator* const pValidator)
{
if (fContentSpec) {
ContentSpecNode* specNode = new ContentSpecNode(*fContentSpec);
XMLContentModel* cm = makeContentModel(true, specNode);
if (cm) {
cm->checkUniqueParticleAttribution(pGrammar, pGrammarResolver, pStringPool, pValidator, fContentSpecOrgURI);
delete cm;
}
fSpecNodesToDelete->removeAllElements();
}
}
// ---------------------------------------------------------------------------
// ComplexTypeInfo: Private Helper methods
// ---------------------------------------------------------------------------
void ComplexTypeInfo::faultInAttDefList() const
{
// Use a hash modulus of 29 and tell it owns its elements
((ComplexTypeInfo*)this)->fAttDefs =
new RefHash2KeysTableOf<SchemaAttDef>(29, true);
}
XMLCh* ComplexTypeInfo::formatContentModel() const
{
XMLCh* newValue = 0;
if (fContentType == SchemaElementDecl::Any)
{
newValue = XMLString::replicate(XMLUni::fgAnyString);
}
else if (fContentType == SchemaElementDecl::Empty)
{
newValue = XMLString::replicate(XMLUni::fgEmptyString);
}
else
{
//
// Use a temp XML buffer to format into. Content models could be
// pretty long, but very few will be longer than one K. The buffer
// will expand to handle the more pathological ones.
//
const ContentSpecNode* specNode = fContentSpec;
if (specNode) {
XMLBuffer bufFmt;
specNode->formatSpec(bufFmt);
newValue = XMLString::replicate(bufFmt.getRawBuffer());
}
}
return newValue;
}
XMLContentModel* ComplexTypeInfo::makeContentModel(const bool checkUPA, ContentSpecNode* const specNode)
{
if ((specNode || fContentSpec) && !fSpecNodesToDelete) {
fSpecNodesToDelete = new RefVectorOf<ContentSpecNode>(8);
}
// expand the content spec first
ContentSpecNode* aSpecNode = specNode;
if (aSpecNode) {
fContentSpecOrgURI = new unsigned int[fContentSpecOrgURISize];
aSpecNode = convertContentSpecTree(aSpecNode, checkUPA);
fSpecNodesToDelete->addElement(aSpecNode);
}
else {
aSpecNode = convertContentSpecTree(fContentSpec, checkUPA);
if (aSpecNode != fContentSpec) {
if (!fAdoptContentSpec && (aSpecNode == fContentSpec->getFirst()))
fAdoptContentSpec = false;
else
fAdoptContentSpec = true;
fContentSpec = aSpecNode;
}
}
XMLContentModel* cmRet = 0;
if (fContentType == SchemaElementDecl::Simple) {
// just return nothing
}
else if (fContentType == SchemaElementDecl::Mixed_Simple)
{
//
// Just create a mixel content model object. This type of
// content model is optimized for mixed content validation.
//
cmRet = new MixedContentModel(false, aSpecNode);
}
else if (fContentType == SchemaElementDecl::Mixed_Complex) {
cmRet = createChildModel(aSpecNode, true);
}
else if (fContentType == SchemaElementDecl::Children)
{
//
// This method will create an optimal model for the complexity
// of the element's defined model. If its simple, it will create
// a SimpleContentModel object. If its a simple list, it will
// create a SimpleListContentModel object. If its complex, it
// will create a DFAContentModel object.
//
cmRet = createChildModel(aSpecNode, false);
}
else
{
ThrowXML(RuntimeException, XMLExcepts::CM_MustBeMixedOrChildren);
}
return cmRet;
}
// ---------------------------------------------------------------------------
// SchemaElementDecl: Private helper methods
// ---------------------------------------------------------------------------
XMLContentModel* ComplexTypeInfo::createChildModel(ContentSpecNode* specNode, const bool isMixed)
{
if(!specNode)
ThrowXML(RuntimeException, XMLExcepts::CM_UnknownCMSpecType);
ContentSpecNode::NodeTypes specType = specNode->getType();
//
// Do a sanity check that the node is does not have a PCDATA id. Since,
// if it was, it should have already gotten taken by the Mixed model.
//
if (specNode->getElement()) {
if (specNode->getElement()->getURI() == XMLElementDecl::fgPCDataElemId)
ThrowXML(RuntimeException, XMLExcepts::CM_NoPCDATAHere);
}
//
// According to the type of node, we will create the correct type of
// content model.
//
if (((specType & 0x0f) == ContentSpecNode::Any) ||
((specType & 0x0f) == ContentSpecNode::Any_Other) ||
((specType & 0x0f) == ContentSpecNode::Any_NS)) {
// let fall through to build a DFAContentModel
}
else if (isMixed)
{
if (specType == ContentSpecNode::All) {
// All the nodes under an ALL must be additional ALL nodes and
// ELEMENTs (or ELEMENTs under ZERO_OR_ONE nodes.)
// We collapse the ELEMENTs into a single vector.
return new AllContentModel(specNode, true);
}
else if (specType == ContentSpecNode::ZeroOrOne) {
// An ALL node can appear under a ZERO_OR_ONE node.
if (specNode->getFirst()->getType() == ContentSpecNode::All) {
return new AllContentModel(specNode->getFirst(), true);
}
}
// otherwise, let fall through to build a DFAContentModel
}
else if (specType == ContentSpecNode::Leaf)
{
// Create a simple content model
return new SimpleContentModel
(
false
, specNode->getElement()
, 0
, ContentSpecNode::Leaf
);
}
else if ((specType == ContentSpecNode::Choice)
|| (specType == ContentSpecNode::Sequence))
{
//
// Lets see if both of the children are leafs. If so, then it has to
// be a simple content model
//
if ((specNode->getFirst()->getType() == ContentSpecNode::Leaf)
&& (specNode->getSecond())
&& (specNode->getSecond()->getType() == ContentSpecNode::Leaf))
{
return new SimpleContentModel
(
false
, specNode->getFirst()->getElement()
, specNode->getSecond()->getElement()
, specType
);
}
}
else if ((specType == ContentSpecNode::OneOrMore)
|| (specType == ContentSpecNode::ZeroOrMore)
|| (specType == ContentSpecNode::ZeroOrOne))
{
//
// Its a repetition, so see if its one child is a leaf. If so its a
// repetition of a single element, so we can do a simple content
// model for that.
//
if (specNode->getFirst()->getType() == ContentSpecNode::Leaf)
{
return new SimpleContentModel
(
false
, specNode->getFirst()->getElement()
, 0
, specType
);
}
else if (specNode->getFirst()->getType() == ContentSpecNode::All)
return new AllContentModel(specNode->getFirst(), false);
}
else if (specType == ContentSpecNode::All)
return new AllContentModel(specNode, false);
else
{
ThrowXML(RuntimeException, XMLExcepts::CM_UnknownCMSpecType);
}
// Its not any simple type of content, so create a DFA based content model
return new DFAContentModel(false, specNode, isMixed);
}
ContentSpecNode*
ComplexTypeInfo::convertContentSpecTree(ContentSpecNode* const curNode,
const bool checkUPA) {
if (!curNode)
return 0;
const ContentSpecNode::NodeTypes curType = curNode->getType();
// When checking Unique Particle Attribution, rename leaf elements
if (checkUPA) {
if (curNode->getElement()) {
fContentSpecOrgURI[fUniqueURI] = curNode->getElement()->getURI();
curNode->getElement()->setURI(fUniqueURI);
fUniqueURI++;
}
if (fUniqueURI == fContentSpecOrgURISize)
resizeContentSpecOrgURI();
}
// Get the spec type of the passed node
int minOccurs = curNode->getMinOccurs();
int maxOccurs = curNode->getMaxOccurs();
ContentSpecNode* retNode = curNode;
if ((curType & 0x0f) == ContentSpecNode::Any
|| (curType & 0x0f) == ContentSpecNode::Any_Other
|| (curType & 0x0f) == ContentSpecNode::Any_NS
|| curType == ContentSpecNode::Leaf)
{
retNode = expandContentModel(curNode, minOccurs, maxOccurs);
}
else if ((curType == ContentSpecNode::Choice)
|| (curType == ContentSpecNode::All)
|| (curType == ContentSpecNode::Sequence))
{
ContentSpecNode* childNode = curNode->getFirst();
ContentSpecNode* leftNode = convertContentSpecTree(childNode, checkUPA);
ContentSpecNode* rightNode = curNode->getSecond();
if (!rightNode) {
retNode = expandContentModel(leftNode, minOccurs, maxOccurs);
curNode->setAdoptFirst(false);
delete curNode;
return retNode;
}
if (leftNode != childNode) {
curNode->setAdoptFirst(false);
curNode->setFirst(leftNode);
curNode->setAdoptFirst(true);
}
childNode = rightNode;
rightNode = convertContentSpecTree(childNode, checkUPA);
if (rightNode != childNode) {
curNode->setAdoptSecond(false);
curNode->setSecond(rightNode);
curNode->setAdoptSecond(true);
}
retNode = expandContentModel(curNode, minOccurs, maxOccurs);
}
return retNode;
}
ContentSpecNode* ComplexTypeInfo::expandContentModel(ContentSpecNode* const specNode,
const int minOccurs,
const int maxOccurs)
{
if (!specNode) {
return 0;
}
ContentSpecNode* saveNode = specNode;
ContentSpecNode* retNode = specNode;
if (minOccurs == 1 && maxOccurs == 1) {
}
else if (minOccurs == 0 && maxOccurs == 1) {
retNode = new ContentSpecNode(ContentSpecNode::ZeroOrOne, retNode, 0);
}
else if (minOccurs == 0 && maxOccurs == -1) {
retNode = new ContentSpecNode(ContentSpecNode::ZeroOrMore, retNode, 0);
}
else if (minOccurs == 1 && maxOccurs == -1) {
retNode = new ContentSpecNode(ContentSpecNode::OneOrMore, retNode, 0);
}
else if (maxOccurs == -1) {
retNode = new ContentSpecNode(ContentSpecNode::OneOrMore, retNode, 0);
for (int i=0; i < (int)(minOccurs-1); i++) {
retNode = new ContentSpecNode(ContentSpecNode::Sequence,
saveNode, retNode, false);
}
}
else {
if (minOccurs == 0) {
ContentSpecNode* optional =
new ContentSpecNode(ContentSpecNode::ZeroOrOne, saveNode, 0);
retNode = optional;
for (int i=0; i < (int)(maxOccurs-minOccurs-1); i++) {
retNode = new ContentSpecNode(ContentSpecNode::Sequence,
retNode, optional, true, false);
}
}
else {
if (minOccurs > 1) {
retNode = new ContentSpecNode(ContentSpecNode::Sequence,
retNode, saveNode, true, false);
for (int i=1; i < (int)(minOccurs-1); i++) {
retNode = new ContentSpecNode(ContentSpecNode::Sequence,
retNode, saveNode, true, false);
}
}
int counter = maxOccurs-minOccurs;
if (counter > 0) {
ContentSpecNode* optional = new ContentSpecNode(ContentSpecNode::ZeroOrOne, saveNode, 0, false);
retNode = new ContentSpecNode(ContentSpecNode::Sequence, retNode, optional);
for (int j=1; j < counter; j++) {
retNode = new ContentSpecNode(ContentSpecNode::Sequence,
retNode, optional, true, false);
}
}
}
}
return retNode;
}
void ComplexTypeInfo::resizeContentSpecOrgURI() {
unsigned int newSize = fContentSpecOrgURISize * 2;
unsigned int* newContentSpecOrgURI = new unsigned int[newSize];
// Copy the existing values
unsigned int index = 0;
for (; index < fContentSpecOrgURISize; index++)
newContentSpecOrgURI[index] = fContentSpecOrgURI[index];
for (; index < newSize; index++)
newContentSpecOrgURI[index] = 0;
// Delete the old array and udpate our members
delete [] fContentSpecOrgURI;
fContentSpecOrgURI = newContentSpecOrgURI;
fContentSpecOrgURISize = newSize;
}
/**
* End of file ComplexTypeInfo.cpp
*/