blob: 6ec4f9a3e3ee44e00517fa6009216408375ba168 [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$
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/validators/schema/identity/XPathMatcher.hpp>
#include <xercesc/validators/schema/identity/XercesXPath.hpp>
#include <xercesc/validators/schema/SchemaElementDecl.hpp>
#include <xercesc/validators/schema/SchemaAttDef.hpp>
#include <xercesc/validators/schema/SchemaSymbols.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/framework/ValidationContext.hpp>
XERCES_CPP_NAMESPACE_BEGIN
typedef JanitorMemFunCall<XPathMatcher> CleanupType;
// ---------------------------------------------------------------------------
// XPathMatcher: Constructors and Destructor
// ---------------------------------------------------------------------------
XPathMatcher::XPathMatcher( XercesXPath* const xpath
, MemoryManager* const manager)
: fLocationPathSize(0)
, fMatched(0)
, fNoMatchDepth(0)
, fCurrentStep(0)
, fStepIndexes(0)
, fLocationPaths(0)
, fIdentityConstraint(0)
, fMemoryManager(manager)
{
CleanupType cleanup(this, &XPathMatcher::cleanUp);
try {
init(xpath);
}
catch(const OutOfMemoryException&)
{
cleanup.release();
throw;
}
cleanup.release();
}
XPathMatcher::XPathMatcher(XercesXPath* const xpath,
IdentityConstraint* const ic,
MemoryManager* const manager)
: fLocationPathSize(0)
, fMatched(0)
, fNoMatchDepth(0)
, fCurrentStep(0)
, fStepIndexes(0)
, fLocationPaths(0)
, fIdentityConstraint(ic)
, fMemoryManager(manager)
{
CleanupType cleanup(this, &XPathMatcher::cleanUp);
try {
init(xpath);
}
catch(const OutOfMemoryException&)
{
cleanup.release();
throw;
}
cleanup.release();
}
XPathMatcher::~XPathMatcher()
{
cleanUp();
}
// ---------------------------------------------------------------------------
// XPathMatcher: Helper methods
// ---------------------------------------------------------------------------
void XPathMatcher::init(XercesXPath* const xpath) {
if (xpath) {
fLocationPaths = xpath->getLocationPaths();
fLocationPathSize = (fLocationPaths ? fLocationPaths->size() : 0);
if (fLocationPathSize) {
fStepIndexes = new (fMemoryManager) RefVectorOf<ValueStackOf<XMLSize_t> >(fLocationPathSize, true, fMemoryManager);
fCurrentStep = (XMLSize_t*) fMemoryManager->allocate
(
fLocationPathSize * sizeof(XMLSize_t)
);//new int[fLocationPathSize];
fNoMatchDepth = (XMLSize_t*) fMemoryManager->allocate
(
fLocationPathSize * sizeof(XMLSize_t)
);//new int[fLocationPathSize];
fMatched = (unsigned char*) fMemoryManager->allocate
(
fLocationPathSize * sizeof(unsigned char)
);//new int[fLocationPathSize];
for(XMLSize_t i=0; i < fLocationPathSize; i++) {
fStepIndexes->addElement(new (fMemoryManager) ValueStackOf<XMLSize_t>(8, fMemoryManager));
}
}
}
}
// ---------------------------------------------------------------------------
// XPathMatcher: XMLDocumentHandler methods
// ---------------------------------------------------------------------------
void XPathMatcher::startDocumentFragment() {
for(XMLSize_t i = 0; i < fLocationPathSize; i++) {
fStepIndexes->elementAt(i)->removeAllElements();
fCurrentStep[i] = 0;
fNoMatchDepth[i] = 0;
fMatched[i] = 0;
}
}
void XPathMatcher::startElement(const XMLElementDecl& elemDecl,
const unsigned int urlId,
const XMLCh* const elemPrefix,
const RefVectorOf<XMLAttr>& attrList,
const XMLSize_t attrCount,
ValidationContext* validationContext /*=0*/) {
for (XMLSize_t i = 0; i < fLocationPathSize; i++) {
// push context
XMLSize_t startStep = fCurrentStep[i];
fStepIndexes->elementAt(i)->push(startStep);
// try next xpath, if not matching
if ((fMatched[i] & XP_MATCHED_D) == XP_MATCHED || fNoMatchDepth[i] > 0) {
fNoMatchDepth[i]++;
continue;
}
if((fMatched[i] & XP_MATCHED_D) == XP_MATCHED_D) {
fMatched[i] = XP_MATCHED_DP;
}
// consume self::node() steps
XercesLocationPath* locPath = fLocationPaths->elementAt(i);
XMLSize_t stepSize = locPath->getStepSize();
while (fCurrentStep[i] < stepSize &&
locPath->getStep(fCurrentStep[i])->getAxisType() == XercesStep::AxisType_SELF) {
fCurrentStep[i]++;
}
if (fCurrentStep[i] == stepSize) {
fMatched[i] = XP_MATCHED;
continue;
}
// now if the current step is a descendant step, we let the next
// step do its thing; if it fails, we reset ourselves
// to look at this step for next time we're called.
// so first consume all descendants:
XMLSize_t descendantStep = fCurrentStep[i];
while (fCurrentStep[i] < stepSize &&
locPath->getStep(fCurrentStep[i])->getAxisType() == XercesStep::AxisType_DESCENDANT) {
fCurrentStep[i]++;
}
bool sawDescendant = fCurrentStep[i] > descendantStep;
if (fCurrentStep[i] == stepSize) {
fNoMatchDepth[i]++;
continue;
}
// match child::... step, if haven't consumed any self::node()
if ((fCurrentStep[i] == startStep || fCurrentStep[i] > descendantStep) &&
locPath->getStep(fCurrentStep[i])->getAxisType() == XercesStep::AxisType_CHILD) {
XercesStep* step = locPath->getStep(fCurrentStep[i]);
XercesNodeTest* nodeTest = step->getNodeTest();
QName elemQName(elemPrefix, elemDecl.getElementName()->getLocalPart(), urlId, fMemoryManager);
if (!matches(nodeTest, &elemQName)) {
if(fCurrentStep[i] > descendantStep) {
fCurrentStep[i] = descendantStep;
continue;
}
fNoMatchDepth[i]++;
continue;
}
fCurrentStep[i]++;
}
if (fCurrentStep[i] == stepSize) {
if (sawDescendant) {
fCurrentStep[i] = descendantStep;
fMatched[i] = XP_MATCHED_D;
}
else {
fMatched[i] = XP_MATCHED;
}
continue;
}
// match attribute::... step
if (fCurrentStep[i] < stepSize &&
locPath->getStep(fCurrentStep[i])->getAxisType() == XercesStep::AxisType_ATTRIBUTE) {
if (attrCount) {
XercesNodeTest* nodeTest = locPath->getStep(fCurrentStep[i])->getNodeTest();
for (XMLSize_t attrIndex = 0; attrIndex < attrCount; attrIndex++) {
const XMLAttr* curDef = attrList.elementAt(attrIndex);
if (matches(nodeTest, curDef->getAttName())) {
fCurrentStep[i]++;
if (fCurrentStep[i] == stepSize) {
fMatched[i] = XP_MATCHED_A;
SchemaAttDef* attDef = ((SchemaElementDecl&) elemDecl).getAttDef(curDef->getName(), curDef->getURIId());
DatatypeValidator* dv = (attDef) ? attDef->getDatatypeValidator() : 0;
const XMLCh* value = curDef->getValue();
// store QName using their Clark name
if(dv && dv->getType()==DatatypeValidator::QName)
{
int index=XMLString::indexOf(value, chColon);
if(index==-1)
matched(value, dv, false);
else
{
XMLBuffer buff(1023, fMemoryManager);
buff.append(chOpenCurly);
if(validationContext)
{
XMLCh* prefix=(XMLCh*)fMemoryManager->allocate((index+1)*sizeof(XMLCh));
ArrayJanitor<XMLCh> janPrefix(prefix, fMemoryManager);
XMLString::subString(prefix, value, 0, (XMLSize_t)index, fMemoryManager);
buff.append(validationContext->getURIForPrefix(prefix));
}
buff.append(chCloseCurly);
buff.append(value+index+1);
matched(buff.getRawBuffer(), dv, false);
}
}
else
matched(value, dv, false);
}
break;
}
}
}
if ((fMatched[i] & XP_MATCHED) != XP_MATCHED) {
if(fCurrentStep[i] > descendantStep) {
fCurrentStep[i] = descendantStep;
continue;
}
fNoMatchDepth[i]++;
}
}
}
}
void XPathMatcher::endElement(const XMLElementDecl& elemDecl,
const XMLCh* const elemContent,
ValidationContext* validationContext /*=0*/,
DatatypeValidator* actualValidator /*=0*/) {
for(XMLSize_t i = 0; i < fLocationPathSize; i++) {
// go back a step
fCurrentStep[i] = fStepIndexes->elementAt(i)->pop();
// don't do anything, if not matching
if (fNoMatchDepth[i] > 0) {
fNoMatchDepth[i]--;
}
// signal match, if appropriate
else {
if (fMatched[i] == 0)
continue;
if ((fMatched[i] & XP_MATCHED_A) == XP_MATCHED_A) {
fMatched[i] = 0;
continue;
}
DatatypeValidator* dv = actualValidator?actualValidator:((SchemaElementDecl*) &elemDecl)->getDatatypeValidator();
bool isNillable = (((SchemaElementDecl *) &elemDecl)->getMiscFlags() & SchemaSymbols::XSD_NILLABLE) != 0;
// store QName using their Clark name
if(dv && dv->getType()==DatatypeValidator::QName)
{
int index=XMLString::indexOf(elemContent, chColon);
if(index==-1)
matched(elemContent, dv, isNillable);
else
{
XMLBuffer buff(1023, fMemoryManager);
buff.append(chOpenCurly);
if(validationContext)
{
XMLCh* prefix=(XMLCh*)fMemoryManager->allocate((index+1)*sizeof(XMLCh));
ArrayJanitor<XMLCh> janPrefix(prefix, fMemoryManager);
XMLString::subString(prefix, elemContent, 0, (XMLSize_t)index, fMemoryManager);
buff.append(validationContext->getURIForPrefix(prefix));
}
buff.append(chCloseCurly);
buff.append(elemContent+index+1);
matched(buff.getRawBuffer(), dv, isNillable);
}
}
else
matched(elemContent, dv, isNillable);
fMatched[i] = 0;
}
}
}
// ---------------------------------------------------------------------------
// XPathMatcher: Match methods
// ---------------------------------------------------------------------------
unsigned char XPathMatcher::isMatched() {
// xpath has been matched if any one of the members of the union have matched.
for (XMLSize_t i=0; i < fLocationPathSize; i++) {
if (((fMatched[i] & XP_MATCHED) == XP_MATCHED)
&& ((fMatched[i] & XP_MATCHED_DP) != XP_MATCHED_DP))
return fMatched[i];
}
return 0;
}
void XPathMatcher::matched(const XMLCh* const,
DatatypeValidator* const,
const bool) {
return;
}
bool XPathMatcher::matches(const XercesNodeTest* nodeTest, const QName* qName)
{
if (nodeTest->getType() == XercesNodeTest::NodeType_QNAME) {
return (*nodeTest->getName())==(*qName);
}
if (nodeTest->getType() == XercesNodeTest::NodeType_NAMESPACE) {
return nodeTest->getName()->getURI() == qName->getURI();
}
// NodeType_WILDCARD
return true;
}
// ---------------------------------------------------------------------------
// XPathMatcher: Match methods
// ---------------------------------------------------------------------------
int XPathMatcher::getInitialDepth() const
{
ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Regex_NotSupported, fMemoryManager);
return 0; // to make some compilers happy
}
XERCES_CPP_NAMESPACE_END
/**
* End of file XPathMatcher.cpp
*/