blob: c7946318b651711b2e74bfaf262bda4f0f1066fc [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2002 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 "Xalan" 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/>.
*
* @author <a href="mailto:david_n_bertoni@lotus.com">David N. Bertoni</a>
*/
// Class header file.
#include "XPath.hpp"
#include <cassert>
#include <cfloat>
#include <Include/STLHelper.hpp>
#include <XalanDOM/XalanNamedNodeMap.hpp>
#include <PlatformSupport/DoubleSupport.hpp>
#include <PlatformSupport/PrefixResolver.hpp>
#include <DOMSupport/DOMServices.hpp>
#include "FoundIndex.hpp"
#include "MutableNodeRefList.hpp"
#include "XalanQNameByReference.hpp"
#include "XLocator.hpp"
#include "XObject.hpp"
#include "XObjectFactory.hpp"
#include "XPathEnvSupport.hpp"
const XalanDOMString XPath::s_emptyString;
XPath::XPath(const Locator* theLocator) :
m_expression(),
m_locator(theLocator),
m_inStylesheet(false)
{
}
XPath::~XPath()
{
}
void
XPath::shrink()
{
m_expression.shrink();
}
void
XPath::installFunction(
const XalanDOMString& funcName,
const Function& func)
{
s_functions.InstallFunction(funcName,
func);
}
bool
XPath::uninstallFunction(const XalanDOMString& funcName)
{
return s_functions.UninstallFunction(funcName);
}
const XObjectPtr
XPath::execute(
XalanNode* context,
const PrefixResolver& prefixResolver,
XPathExecutionContext& executionContext) const
{
// Push and pop the PrefixResolver...
XPathExecutionContext::PrefixResolverSetAndRestore theResolverSetAndRestore(
executionContext,
&prefixResolver);
// Push and pop the current node...
XPathExecutionContext::CurrentNodeSetAndRestore theNodeSetAndRestore(
executionContext,
context);
return executeMore(context, 0, executionContext);
}
const XObjectPtr
XPath::executeMore(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
switch(m_expression.m_opMap[opPos])
{
case XPathExpression::eOP_XPATH:
return executeMore(context, opPos + 2, executionContext);
break;
case XPathExpression::eOP_MATCHPATTERN:
return matchPattern(context, opPos + 2, executionContext);
break;
case XPathExpression::eEMPTY:
opPos++;
break;
case XPathExpression::eOP_OR:
return Or(context, opPos, executionContext);
break;
case XPathExpression::eOP_AND:
return And(context, opPos, executionContext);
break;
case XPathExpression::eOP_NOTEQUALS:
return notequals(context, opPos, executionContext);
break;
case XPathExpression::eOP_EQUALS:
return equals(context, opPos, executionContext);
break;
case XPathExpression::eOP_LTE:
return lte(context, opPos, executionContext);
break;
case XPathExpression::eOP_LT:
return lt(context, opPos, executionContext);
break;
case XPathExpression::eOP_GTE:
return gte(context, opPos, executionContext);
break;
case XPathExpression::eOP_GT:
return gt(context, opPos, executionContext);
break;
case XPathExpression::eOP_PLUS:
return plus(context, opPos, executionContext);
break;
case XPathExpression::eOP_MINUS:
return minus(context, opPos, executionContext);
break;
case XPathExpression::eOP_MULT:
return mult(context, opPos, executionContext);
break;
case XPathExpression::eOP_DIV:
return div(context, opPos, executionContext);
break;
case XPathExpression::eOP_MOD:
return mod(context, opPos, executionContext);
break;
case XPathExpression::eOP_NEG:
return neg(context, opPos, executionContext);
break;
case XPathExpression::eOP_BOOL:
return boolean(context, opPos, executionContext);
break;
case XPathExpression::eOP_UNION:
return Union(context, opPos, executionContext);
break;
case XPathExpression::eOP_LITERAL:
return literal(context, opPos, executionContext);
break;
case XPathExpression::eOP_VARIABLE:
return variable(context, opPos, executionContext);
break;
case XPathExpression::eOP_GROUP:
return group(context, opPos, executionContext);
break;
case XPathExpression::eOP_NUMBERLIT:
return numberlit(context, opPos, executionContext);
break;
case XPathExpression::eOP_ARGUMENT:
return arg(context, opPos, executionContext);
break;
case XPathExpression::eOP_EXTFUNCTION:
return runExtFunction(context, opPos, executionContext);
break;
case XPathExpression::eOP_FUNCTION:
return runFunction(context, opPos, executionContext);
break;
case XPathExpression::eOP_LOCATIONPATH:
return locationPath(context, opPos, executionContext);
break;
case XPathExpression::eOP_LOCATIONPATHPATTERN:
return locationPathPattern(context, opPos, executionContext);
break;
default:
{
const XalanDOMString theOpCode = LongToDOMString(m_expression.m_opMap[opPos]);
executionContext.error(
TranscodeFromLocalCodePage("ERROR! Unknown op code: ") + theOpCode,
context,
m_locator);
}
break;
}
return XObjectPtr();
}
inline void
XPath::doGetMatchScore(
XalanNode* context,
XPathExecutionContext& executionContext,
eMatchScore& score) const
{
assert(context != 0);
int opPos = 2;
while(m_expression.m_opMap[opPos] == XPathExpression::eOP_LOCATIONPATHPATTERN)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
score = locationPathPattern(executionContext, *context, opPos);
if(score == eMatchScoreNone)
{
opPos = nextOpPos;
}
else
{
break;
}
}
}
XPath::eMatchScore
XPath::getMatchScore(
XalanNode* node,
const PrefixResolver& resolver,
XPathExecutionContext& executionContext) const
{
eMatchScore score = eMatchScoreNone;
if(m_expression.m_opMap[0] == XPathExpression::eOP_MATCHPATTERN)
{
assert(node != 0);
const PrefixResolver* const theCurrentResolver =
executionContext.getPrefixResolver();
if (theCurrentResolver == &resolver)
{
doGetMatchScore(node, executionContext, score);
}
else
{
// Push and pop the PrefixResolver...
XPathExecutionContext::PrefixResolverSetAndRestore theSetAndRestore(
executionContext,
theCurrentResolver,
&resolver);
doGetMatchScore(node, executionContext, score);
}
}
else
{
executionContext.error(
TranscodeFromLocalCodePage("Expected match pattern in getMatchScore!"),
node,
m_locator);
}
return score;
}
void
XPath::getTargetElementStrings(TargetElementStringsVectorType& targetStrings) const
{
int opPos = 2;
targetStrings.reserve(eDefaultTargetStringsSize);
while(m_expression.m_opMap[opPos] == XPathExpression::eOP_LOCATIONPATHPATTERN)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
opPos += 2;
while(m_expression.m_opMap[opPos] != XPathExpression::eENDOP)
{
const int nextStepPos =
m_expression.getNextOpCodePosition(opPos);
const int nextOp = m_expression.m_opMap[nextStepPos];
if(nextOp == XPathExpression::eOP_PREDICATE ||
nextOp == XPathExpression::eENDOP)
{
const int stepType = m_expression.m_opMap[opPos];
opPos += 3;
switch(stepType)
{
case XPathExpression::eOP_FUNCTION:
targetStrings.push_back(PSEUDONAME_ANY);
break;
case XPathExpression::eFROM_ROOT:
targetStrings.push_back(PSEUDONAME_ROOT);
break;
case XPathExpression::eMATCH_ATTRIBUTE:
case XPathExpression::eMATCH_ANY_ANCESTOR:
case XPathExpression::eMATCH_IMMEDIATE_ANCESTOR:
{
const int tok = m_expression.m_opMap[opPos];
opPos++;
switch(tok)
{
case XPathExpression::eNODETYPE_COMMENT:
targetStrings.push_back(PSEUDONAME_COMMENT);
break;
case XPathExpression::eNODETYPE_TEXT:
targetStrings.push_back(PSEUDONAME_TEXT);
break;
case XPathExpression::eNODETYPE_NODE:
targetStrings.push_back(PSEUDONAME_NODE);
break;
case XPathExpression::eNODETYPE_ROOT:
targetStrings.push_back(PSEUDONAME_ROOT);
break;
case XPathExpression::eNODETYPE_ANYELEMENT:
targetStrings.push_back(PSEUDONAME_ANY);
break;
case XPathExpression::eNODETYPE_PI:
targetStrings.push_back(PSEUDONAME_PI);
break;
case XPathExpression::eNODENAME:
{
// Skip the namespace
const int tokenIndex = m_expression.m_opMap[opPos + 1];
if(tokenIndex >= 0)
{
const XalanDOMString& targetName =
m_expression.m_tokenQueue[tokenIndex].str();
if(::equals(targetName, PSEUDONAME_ANY) == true)
{
targetStrings.push_back(PSEUDONAME_ANY);
}
else
{
targetStrings.push_back(targetName);
}
}
else
{
targetStrings.push_back(PSEUDONAME_ANY);
}
}
break;
default:
targetStrings.push_back(PSEUDONAME_ANY);
break;
}
}
break;
}
}
opPos = nextStepPos;
}
opPos = nextOpPos;
}
}
const XObjectPtr
XPath::matchPattern(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
XObjectPtr score;
while(m_expression.m_opMap[opPos] == XPathExpression::eOP_LOCATIONPATHPATTERN)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
score = executeMore(context, opPos, executionContext);
assert(score.null() == false);
if(score->num() != eMatchScoreNone)
{
break;
}
else
{
opPos = nextOpPos;
}
}
if(score.null() == false)
{
return score;
}
else
{
return executionContext.getXObjectFactory().createNumber(getMatchScoreValue(eMatchScoreNone));
}
}
const XObjectPtr
XPath::Or(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
bool fResult = expr1->boolean();
if(fResult == false)
{
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
fResult = expr2->boolean();
}
return executionContext.getXObjectFactory().createBoolean(fResult);
}
const XObjectPtr
XPath::And(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
bool fResult = false;
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
if (expr1->boolean() == true)
{
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
if (expr2->boolean() == true)
{
fResult = true;
}
}
return executionContext.getXObjectFactory().createBoolean(fResult);
}
const XObjectPtr
XPath::notequals(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->notEquals(*expr2.get(), executionContext));
}
const XObjectPtr
XPath::equals(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->equals(*expr2.get(), executionContext));
}
const XObjectPtr
XPath::lte(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->lessThanOrEquals(*expr2.get(), executionContext));
}
const XObjectPtr
XPath::lt(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->lessThan(*expr2.get(), executionContext));
}
const XObjectPtr
XPath::gte(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->greaterThanOrEquals(*expr2.get(), executionContext));
}
const XObjectPtr
XPath::gt(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createBoolean(expr1->greaterThan(*expr2.get(), executionContext));
}
double
XPath::getNumericOperand(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
if (m_expression.m_opMap[opPos] == XPathExpression::eOP_NUMBERLIT)
{
assert(m_expression.m_tokenQueue.size() > unsigned(m_expression.m_opMap[opPos + 3]));
return m_expression.getNumberLiteral(m_expression.m_opMap[opPos + 2]);
}
else
{
return executeMore(context, opPos, executionContext)->num();
}
}
const XObjectPtr
XPath::plus(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const double expr1 = getNumericOperand(context, opPos, executionContext);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const double expr2 = getNumericOperand(context, expr2Pos, executionContext);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::add(expr1, expr2));
}
const XObjectPtr
XPath::minus(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const double expr1 = getNumericOperand(context, opPos, executionContext);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const double expr2 = getNumericOperand(context, expr2Pos, executionContext);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::subtract(expr1, expr2));
}
const XObjectPtr
XPath::mult(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const double expr1 = getNumericOperand(context, opPos, executionContext);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const double expr2 = getNumericOperand(context, expr2Pos, executionContext);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::multiply(expr1, expr2));
}
const XObjectPtr
XPath::div(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const double expr1 = getNumericOperand(context, opPos, executionContext);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const double expr2 = getNumericOperand(context, expr2Pos, executionContext);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::divide(expr1, expr2));
}
const XObjectPtr
XPath::mod(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
const XObjectPtr expr1(executeMore(context, opPos, executionContext));
assert(expr1.get() != 0);
const int expr2Pos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr2(executeMore(context, expr2Pos, executionContext));
assert(expr2.get() != 0);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::modulus(expr1->num(), expr2->num()));
}
const XObjectPtr
XPath::neg(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
const XObjectPtr expr1(executeMore(context, opPos + 2, executionContext));
assert(expr1.get() != 0);
return executionContext.getXObjectFactory().createNumber(DoubleSupport::negative(expr1->num()));
}
const XObjectPtr
XPath::boolean(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
const XObjectPtr expr1(executeMore(context, opPos + 2, executionContext));
assert(expr1.get() != 0);
// Try to optimize when the result of the execution is
// already a boolean.
if (expr1->getType() == XObject::eTypeBoolean)
{
return expr1;
}
else
{
return executionContext.getXObjectFactory().createBoolean(expr1->boolean());
}
}
const XObjectPtr
XPath::Union(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
opPos += 2;
typedef XPathExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList;
BorrowReturnMutableNodeRefList resultNodeList(executionContext);
while(m_expression.m_opMap[opPos] != XPathExpression::eENDOP)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr expr(executeMore(context, opPos, executionContext));
const NodeRefListBase& nl =
expr->nodeset();
resultNodeList->addNodesInDocOrder(nl, executionContext);
opPos = nextOpPos;
}
return executionContext.getXObjectFactory().createNodeSet(resultNodeList);
}
const XObjectPtr
XPath::literal(
XalanNode* /* context */,
int opPos,
XPathExecutionContext& executionContext) const
{
assert(m_expression.m_opMap.size() > unsigned(opPos + 2));
assert(m_expression.m_tokenQueue.size() > unsigned(m_expression.m_opMap[opPos + 2]));
const XToken& theLiteral = m_expression.m_tokenQueue[m_expression.m_opMap[opPos + 2]];
if (m_inStylesheet == true)
{
return executionContext.getXObjectFactory().createString(theLiteral);
}
else
{
return executionContext.getXObjectFactory().createString(theLiteral.str());
}
}
const XObjectPtr
XPath::variable(
XalanNode* /* context */,
int opPos,
XPathExecutionContext& executionContext) const
{
assert(executionContext.getPrefixResolver() != 0);
const XObject& ns = m_expression.m_tokenQueue[m_expression.m_opMap[opPos + 2]];
const XObject& varName = m_expression.m_tokenQueue[m_expression.m_opMap[opPos + 3]];
return executionContext.getVariable(XalanQNameByReference(ns.str(), varName.str()), m_locator);
}
const XObjectPtr
XPath::numberlit(
XalanNode* /* context */,
int opPos,
XPathExecutionContext& executionContext) const
{
assert(m_expression.m_opMap.size() > unsigned(opPos + 3));
assert(m_expression.m_tokenQueue.size() > unsigned(m_expression.m_opMap[opPos + 3]));
const XToken& theLiteral = m_expression.m_tokenQueue[m_expression.m_opMap[opPos + 3]];
if (m_inStylesheet == true)
{
return executionContext.getXObjectFactory().createNumber(theLiteral);
}
else
{
return executionContext.getXObjectFactory().createNumber(theLiteral.num());
}
}
const XObjectPtr
XPath::locationPath(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
assert(context != 0);
return locationPath(executionContext, *context, opPos);
}
const XObjectPtr
XPath::locationPathPattern(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
assert(context != 0);
const eMatchScore result =
locationPathPattern(executionContext, *context, opPos);
return executionContext.getXObjectFactory().createNumber(getMatchScoreValue(result));
}
const XObjectPtr
XPath::runExtFunction(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
const int endExtFunc = opPos + m_expression.m_opMap[opPos + 1] - 1;
opPos += 2;
const XalanDOMString& ns = m_expression.m_tokenQueue[m_expression.m_opMap[opPos]].str();
++opPos;
const XalanDOMString& funcName = m_expression.m_tokenQueue[m_expression.m_opMap[opPos]].str();
++opPos;
typedef XPathExecutionContext::XObjectArgVectorType XObjectArgVectorType;
XObjectArgVectorType args;
while(opPos < endExtFunc)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
args.push_back(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
}
return extfunction(context, opPos, ns, funcName, args, executionContext);
}
const XObjectPtr
XPath::runFunction(
XalanNode* context,
int opPos,
XPathExecutionContext& executionContext) const
{
const int endFunc = opPos + m_expression.m_opMap[opPos + 1] - 1;
opPos += 2;
// This is actually the position in the token queue of the
// string that contains the name of the function.
const int funcID = m_expression.m_opMap[opPos];
opPos++;
// Number of args is next, not used for now...
const int argCount = m_expression.m_opMap[opPos];
opPos++;
if (argCount == 0)
{
assert(opPos == endFunc);
return s_functions[funcID].execute(executionContext, context, m_locator);
}
else if (argCount == 1)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
assert(opPos == endFunc);
return s_functions[funcID].execute(executionContext, context, theArg, m_locator);
}
else if (argCount == 2)
{
int nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg1(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg2(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
assert(opPos == endFunc);
return s_functions[funcID].execute(executionContext, context, theArg1, theArg2, m_locator);
}
else if (argCount == 3)
{
int nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg1(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg2(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
nextOpPos = m_expression.getNextOpCodePosition(opPos);
const XObjectPtr theArg3(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
assert(opPos == endFunc);
return s_functions[funcID].execute(executionContext, context, theArg1, theArg2, theArg3, m_locator);
}
else
{
typedef XPathExecutionContext::XObjectArgVectorType XObjectArgVectorType;
XObjectArgVectorType args;
args.reserve(argCount);
while(opPos < endFunc)
{
const int nextOpPos = m_expression.getNextOpCodePosition(opPos);
args.push_back(executeMore(context, opPos, executionContext));
opPos = nextOpPos;
}
return function(context, funcID, args, executionContext);
}
}
const XObjectPtr
XPath::locationPath(
XPathExecutionContext& executionContext,
XalanNode& context,
int opPos) const
{
typedef XPathExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList;
BorrowReturnMutableNodeRefList mnl(executionContext);
step(executionContext, &context, opPos + 2, *mnl.get());
return executionContext.getXObjectFactory().createNodeSet(mnl);
}
XPath::eMatchScore
XPath::locationPathPattern(
XPathExecutionContext& executionContext,
XalanNode& context,
int opPos) const
{
eMatchScore score = eMatchScoreNone;
stepPattern(executionContext, &context, opPos + 2, score);
return score;
}
void
XPath::step(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
MutableNodeRefList& queryResults) const
{
const XPathExpression& currentExpression = getExpression();
// int endStep = xpath.getNextOpPos(opPos);
const int stepType =
currentExpression.getOpCodeMapValue(opPos);
int argLen = 0;
typedef XPathExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList;
BorrowReturnMutableNodeRefList subQueryResults(executionContext);
bool shouldReorder = false;
bool continueStepRecursion = true;
switch(stepType)
{
case XPathExpression::eOP_VARIABLE:
case XPathExpression::eOP_EXTFUNCTION:
case XPathExpression::eOP_FUNCTION:
case XPathExpression::eOP_GROUP:
argLen = findNodeSet(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_ROOT:
argLen = findRoot(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_PARENT:
argLen = findParent(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_SELF:
argLen = findSelf(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_ANCESTORS:
argLen = findAncestors(executionContext, context, opPos, stepType, *subQueryResults);
shouldReorder = true;
break;
case XPathExpression::eFROM_ANCESTORS_OR_SELF:
argLen = findAncestorsOrSelf(executionContext, context, opPos, stepType, *subQueryResults);
shouldReorder = true;
break;
case XPathExpression::eMATCH_ATTRIBUTE:
continueStepRecursion = false;
// fall-through on purpose.
case XPathExpression::eFROM_ATTRIBUTES:
argLen = findAttributes(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eMATCH_ANY_ANCESTOR:
case XPathExpression::eMATCH_IMMEDIATE_ANCESTOR:
case XPathExpression::eMATCH_ANY_ANCESTOR_WITH_PREDICATE:
continueStepRecursion = false;
// fall-through on purpose.
case XPathExpression::eFROM_CHILDREN:
argLen = findChildren(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_DESCENDANTS:
case XPathExpression::eFROM_DESCENDANTS_OR_SELF:
argLen = findDescendants(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_FOLLOWING:
argLen = findFollowing(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_FOLLOWING_SIBLINGS:
argLen = findFollowingSiblings(executionContext, context, opPos, stepType, *subQueryResults);
break;
case XPathExpression::eFROM_PRECEDING:
argLen = findPreceeding(executionContext, context, opPos, stepType, *subQueryResults);
shouldReorder = true;
break;
case XPathExpression::eFROM_PRECEDING_SIBLINGS:
argLen = findPreceedingSiblings(executionContext, context, opPos, stepType, *subQueryResults);
shouldReorder = true;
break;
case XPathExpression::eFROM_NAMESPACE:
argLen = findNamespace(executionContext, context, opPos, stepType, *subQueryResults);
break;
default:
argLen = findNodesOnUnknownAxis(executionContext, context, opPos, stepType, *subQueryResults);
break;
}
// Push and pop the PrefixResolver...
XPathExecutionContext::ContextNodeListSetAndRestore theSetAndRestore(
executionContext,
*subQueryResults);
opPos += argLen;
int nextStepType = currentExpression.getOpCodeMapValue(opPos);
if(XPathExpression::eOP_PREDICATE == nextStepType)
{
predicates(executionContext,
context,
opPos,
*subQueryResults,
opPos);
nextStepType = currentExpression.getOpCodeMapValue(opPos);
}
if(XPathExpression::eENDOP != nextStepType && continueStepRecursion == true)
{
const NodeRefListBase::size_type nContexts = subQueryResults->getLength();
for(NodeRefListBase::size_type i = 0; i < nContexts; i++)
{
XalanNode* const node = subQueryResults->item(i);
if(0 != node)
{
BorrowReturnMutableNodeRefList mnl(executionContext);
step(executionContext, node, opPos, *mnl);
if (mnl->getLength() > 0)
{
if(queryResults.getLength() == 0)
{
if (mnl->getReverseDocumentOrder() == true)
{
mnl->reverse();
queryResults.setDocumentOrder();
}
else if (mnl->getDocumentOrder() == true)
{
queryResults.setDocumentOrder();
}
queryResults = *mnl;
}
else
{
queryResults.addNodesInDocOrder(*mnl, executionContext);
queryResults.setDocumentOrder();
}
}
}
}
}
else
{
if (shouldReorder == true)
{
if (queryResults.getLength() != 0 ||
subQueryResults->getUnknownOrder() == true)
{
queryResults.addNodesInDocOrder(*subQueryResults, executionContext);
}
else
{
if (subQueryResults->getReverseDocumentOrder() == true)
{
subQueryResults->reverse();
queryResults = *subQueryResults;
}
else if (subQueryResults->getDocumentOrder() == true)
{
queryResults = *subQueryResults;
}
else
{
assert(false);
}
}
queryResults.setDocumentOrder();
}
else
{
queryResults = *subQueryResults;
}
}
}
XalanNode*
XPath::stepPattern(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
eMatchScore& scoreHolder) const
{
const XPathExpression& currentExpression = getExpression();
const int endStep = currentExpression.getNextOpCodePosition(opPos);
int nextStepType = currentExpression.getOpCodeMapValue(endStep);
if(XPathExpression::eENDOP != nextStepType)
{
// Continue step via recursion...
context = stepPattern(
executionContext,
context,
endStep,
scoreHolder);
if(0 == context)
{
scoreHolder = eMatchScoreNone;
}
if (scoreHolder == eMatchScoreNone)
{
// !!!!!!!!!!!!! Big ugly return here !!!!!!!!!!!!!!!!!!!
return 0;
}
scoreHolder = eMatchScoreOther;
if (nextStepType != XPathExpression::eMATCH_ANY_ANCESTOR_WITH_FUNCTION_CALL)
{
context = DOMServices::getParentOfNode(*context);
}
if(0 == context)
{
// !!!!!!!!!!!!! Big ugly return here !!!!!!!!!!!!!!!!!!!
return 0;
}
}
assert(context != 0);
int argLen = 0;
eMatchScore score = eMatchScoreNone;
const int startOpPos = opPos;
const int stepType = currentExpression.getOpCodeMapValue(opPos);
switch(stepType)
{
case XPathExpression::eMATCH_ANY_ANCESTOR_WITH_FUNCTION_CALL:
score = scoreHolder;
break;
case XPathExpression::eOP_FUNCTION:
{
argLen = currentExpression.getOpCodeLengthFromOpMap(opPos);
const XObjectPtr obj(executeMore(context, opPos, executionContext));
assert(obj.get() != 0);
const NodeRefListBase& nl = obj->nodeset();
const NodeRefListBase::size_type len = nl.getLength();
if (nextStepType == XPathExpression::eMATCH_ANY_ANCESTOR_WITH_FUNCTION_CALL)
{
bool fFound = false;
while(context != 0 && fFound == false)
{
for(NodeRefListBase::size_type i = 0; i < len; i++)
{
XalanNode* const n = nl.item(i);
if(n == context)
{
score = eMatchScoreOther;
context = n;
fFound = true;
break;
}
}
context = DOMServices::getParentOfNode(*context);
}
}
else
{
for(NodeRefListBase::size_type i = 0; i < len; i++)
{
XalanNode* const n = nl.item(i);
if(n == context)
{
score = eMatchScoreOther;
context = n;
break;
}
}
}
}
break;
case XPathExpression::eFROM_ROOT:
{
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
opPos += 3;
const XalanNode::NodeType nodeType = context->getNodeType();
if (nodeType == XalanNode::DOCUMENT_NODE ||
nodeType == XalanNode::DOCUMENT_FRAGMENT_NODE)
{
score = eMatchScoreOther;
}
else
{
const int prevPos = currentExpression.getNextOpCodePosition(startOpPos);
const int prevStepType = currentExpression.getOpCodeMapValue(prevPos);
if (eMatchScoreNone == score &&
(prevStepType == XPathExpression::eMATCH_ANY_ANCESTOR ||
prevStepType == XPathExpression::eMATCH_ANY_ANCESTOR_WITH_PREDICATE))
{
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
while(0 != context)
{
score =
theTester(*context, context->getNodeType());
assert(score == nodeTest(executionContext, context, context->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
break;
context = DOMServices::getParentOfNode(*context);
}
}
}
}
break;
case XPathExpression::eMATCH_ATTRIBUTE:
{
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
opPos += 3;
score = NodeTester(
*this,
executionContext,
opPos,
argLen,
XPathExpression::eFROM_ATTRIBUTES)(*context, context->getNodeType());
assert(score == nodeTest(
executionContext,
context,
context->getNodeType(),
opPos,
argLen,
XPathExpression::eFROM_ATTRIBUTES));
}
break;
case XPathExpression::eMATCH_ANY_ANCESTOR:
case XPathExpression::eMATCH_ANY_ANCESTOR_WITH_PREDICATE:
{
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
XalanNode::NodeType nodeType = context->getNodeType();
if(nodeType != XalanNode::ATTRIBUTE_NODE)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
for(;;)
{
score = theTester(*context, nodeType);
assert(score == nodeTest(executionContext, context, nodeType, opPos, argLen, stepType));
if(eMatchScoreNone != score)
break;
context = DOMServices::getParentOfNode(*context);
if (context == 0)
break;
nodeType = context->getNodeType();
}
}
}
break;
case XPathExpression::eMATCH_IMMEDIATE_ANCESTOR:
{
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
const XalanNode::NodeType nodeType = context->getNodeType();
if(nodeType != XalanNode::ATTRIBUTE_NODE)
{
opPos += 3;
score = NodeTester(
*this,
executionContext,
opPos,
argLen,
XPathExpression::eMATCH_IMMEDIATE_ANCESTOR)(*context, nodeType);
assert(score == nodeTest(
executionContext,
context,
nodeType,
opPos,
argLen,
XPathExpression::eMATCH_IMMEDIATE_ANCESTOR));
}
}
break;
default:
executionContext.error(TranscodeFromLocalCodePage("unknown match operation!"), context, getLocator());
break;
}
opPos += argLen;
nextStepType = currentExpression.getOpCodeMapValue(opPos);
if(score != eMatchScoreNone && XPathExpression::eOP_PREDICATE == nextStepType)
{
score = eMatchScoreOther;
// Execute the xpath.predicates, but if we have an index, then we have
// to start over and do a search from the parent. It would be nice
// if I could sense this condition earlier...
try
{
executionContext.setThrowFoundIndex(true);
while(XPathExpression::eOP_PREDICATE == nextStepType)
{
// This is a quick hack to look ahead and see if we have
// number literal as the predicate, i.e. match="foo[1]".
if (m_expression.getOpCodeMapValue(opPos + 2) == XPathExpression::eOP_NUMBERLIT)
{
score = handleFoundIndex(executionContext, context, startOpPos);
}
else
{
const XObjectPtr pred(predicate(context, opPos, executionContext));
assert(pred.get() != 0);
if(XObject::eTypeNumber == pred->getType())
{
score = handleFoundIndex(executionContext, context, startOpPos);
}
else if(pred->boolean() == false)
{
score = eMatchScoreNone;
break;
}
}
opPos = currentExpression.getNextOpCodePosition(opPos);
nextStepType = currentExpression.getOpCodeMapValue(opPos);
}
executionContext.setThrowFoundIndex(false);
}
catch(const FoundIndex&)
{
score = handleFoundIndex(executionContext, context, startOpPos);
}
}
if (scoreHolder == eMatchScoreNone ||
score == eMatchScoreNone)
{
scoreHolder = score;
}
return score == eMatchScoreNone ? 0 : context;
}
XPath::eMatchScore
XPath::handleFoundIndex(
XPathExecutionContext& executionContext,
XalanNode* localContext,
int startOpPos) const
{
// We have an index somewhere in our pattern. So, we have
// to do a full search for our step, using the parent as
// localContext, then see if the current localContext is found in the
// node set. Seems crazy, but, so far, it seems like the
// easiest way.
executionContext.setThrowFoundIndex(false);
XalanNode* const parentContext =
DOMServices::getParentOfNode(*localContext);
if (parentContext == 0)
{
return eMatchScoreNone;
}
else
{
typedef XPathExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList;
BorrowReturnMutableNodeRefList mnl(executionContext);
step(executionContext, parentContext, startOpPos, *mnl);
if (mnl->indexOf(localContext) == MutableNodeRefList::npos)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreOther;
}
}
}
int
XPath::findNodeSet(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int /* stepType */,
MutableNodeRefList& subQueryResults) const
{
const XPathExpression& currentExpression = getExpression();
const XObjectPtr obj(executeMore(context, opPos, executionContext));
const NodeRefListBase& nl = obj->nodeset();
subQueryResults.addNodes(nl);
return currentExpression.getOpCodeLengthFromOpMap(opPos);
}
int
XPath::findRoot(
XPathExecutionContext& /* executionContext */,
XalanNode* context,
int opPos,
int /* stepType */,
MutableNodeRefList& subQueryResults) const
{
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
XalanNode* const docContext = XalanNode::DOCUMENT_NODE == context->getNodeType() ?
context :
context->getOwnerDocument();
assert(docContext != 0);
subQueryResults.addNode(docContext);
subQueryResults.setDocumentOrder();
return argLen + 3;
}
int
XPath::findParent(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
XalanNode* const theParent = DOMServices::getParentOfNode(*context);
if(0 != theParent)
{
if(argLen > 0)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
const eMatchScore score = theTester(*theParent, theParent->getNodeType());
assert(score == nodeTest(executionContext, theParent, theParent->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(theParent);
}
}
else
{
subQueryResults.addNode(theParent);
}
}
subQueryResults.setDocumentOrder();
return argLen + 3;
}
int
XPath::findSelf(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
if(argLen == 0)
{
subQueryResults.addNode(context);
}
else
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
const eMatchScore score =
theTester(*context, context->getNodeType());
assert(score == nodeTest(executionContext, context, context->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(context);
}
}
subQueryResults.setDocumentOrder();
return argLen + 3;
}
int
XPath::findAncestors(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
context = DOMServices::getParentOfNode(*context);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
if (context != 0)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const eMatchScore score =
theTester(*context, context->getNodeType());
assert(score == nodeTest(executionContext, context, context->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(context);
}
context = DOMServices::getParentOfNode(*context);
} while(0 != context);
subQueryResults.setReverseDocumentOrder();
}
return argLen + 3;
}
int
XPath::findAncestorsOrSelf(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const eMatchScore score =
theTester(*context, context->getNodeType());
assert(score == nodeTest(executionContext, context, context->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(context);
}
context = DOMServices::getParentOfNode(*context);
} while(0 != context);
subQueryResults.setReverseDocumentOrder();
return argLen + 3;
}
int
XPath::findAttributes(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
if(context->getNodeType() == XalanNode::ELEMENT_NODE)
{
const XalanNamedNodeMap* const attributeList = context->getAttributes();
if(attributeList != 0)
{
opPos += 3;
const unsigned int nAttrs = attributeList->getLength();
if (nAttrs != 0)
{
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
for(unsigned int j = 0; j < nAttrs; j++)
{
XalanNode* const theNode = attributeList->item(j);
assert(theNode != 0 && theNode->getNodeType() == XalanNode::ATTRIBUTE_NODE);
const eMatchScore score =
theTester(*theNode, XalanNode::ATTRIBUTE_NODE);
assert(score == nodeTest(executionContext, theNode, XalanNode::ATTRIBUTE_NODE, opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(theNode);
}
}
}
}
subQueryResults.setDocumentOrder();
}
return argLen + 3;
}
int
XPath::findChildren(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
XalanNode* child = context->getFirstChild();
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
if (child != 0)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const eMatchScore score =
theTester(*child, child->getNodeType());
assert(score == nodeTest(executionContext, child, child->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(child);
}
child = child->getNextSibling();
} while(0 != child);
subQueryResults.setDocumentOrder();
}
return argLen + 3;
}
int
XPath::findDescendants(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
// Perform a pre-order traversal of descendents...
XalanNode* pos = context;
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
if(stepType == XPathExpression::eFROM_DESCENDANTS_OR_SELF ||
context != pos)
{
const eMatchScore score =
theTester(*pos, pos->getNodeType());
assert(score == nodeTest(executionContext, pos, pos->getNodeType(), opPos, argLen, stepType));
if(score != eMatchScoreNone)
{
subQueryResults.addNode(pos);
}
}
XalanNode* nextNode = pos->getFirstChild();
while(0 == nextNode)
{
if(context == pos)
break;
nextNode = pos->getNextSibling();
if(0 == nextNode)
{
pos = DOMServices::getParentOfNode(*pos);
if(context == pos || pos == 0)
{
nextNode = 0;
break;
}
}
}
pos = nextNode;
} while(0 != pos);
subQueryResults.setDocumentOrder();
return argLen + 3;
}
int
XPath::findFollowing(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
opPos += 3;
// What fun...
XalanDocument* const doc = context->getOwnerDocument();
XalanNode* pos = context;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
while(0 != pos)
{
XalanNode* nextNode = 0;
if(pos != context)
{
const eMatchScore score =
theTester(*pos, pos->getNodeType());
assert(score == nodeTest(executionContext, pos, pos->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNodeInDocOrder(pos, executionContext);
}
nextNode = pos->getFirstChild();
}
else
{
nextNode = 0;
}
while(0 == nextNode)
{
// This requires some explanation. pos could be an attribute
// node, so getNextSibling() will always return 0. In that
// case, I want to continue the search with the first child of
// the owner element, as if attribute nodes are children which
// are always _before_ the first child element. I don't have to
// consider following attributes, since they _never_ match the
// following axis.
if (pos->getNodeType() == XalanNode::ATTRIBUTE_NODE)
{
assert(DOMServices::getParentOfNode(*pos) != 0);
nextNode = DOMServices::getParentOfNode(*pos)->getFirstChild();
}
else
{
nextNode = pos->getNextSibling();
}
if(0 == nextNode)
{
pos = DOMServices::getParentOfNode(*pos);
if(doc == pos || 0 == pos)
{
nextNode = 0;
break;
}
}
}
pos = nextNode;
}
subQueryResults.setDocumentOrder();
return argLen + 3;
}
int
XPath::findFollowingSiblings(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
XalanNode* pos = context->getNextSibling();
if (pos != 0)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const eMatchScore score =
theTester(*pos, pos->getNodeType());
assert(score == nodeTest(executionContext, pos, pos->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(pos);
}
pos = pos->getNextSibling();
} while(0 != pos);
subQueryResults.setDocumentOrder();
}
return argLen + 3;
}
/*
* This is bit of a hack to figure out where we should start for
* the preceeding axis. It's mostly to support RTFs which have
* been coerced to nodesets through the nodeset() extension function.
*/
static XalanNode*
findTopNode(
XalanNode* context,
XalanNode::NodeType theType)
{
assert(context != 0);
if (theType == XalanNode::DOCUMENT_NODE)
{
return context;
}
else
{
XalanDocument* const theDocument = context->getOwnerDocument();
assert(theDocument != 0);
if (theDocument->getDocumentElement() != 0)
{
return theDocument;
}
else
{
XalanNode* theNode = 0;
while(context != 0 &&
context->getNodeType() != XalanNode::DOCUMENT_NODE)
{
theNode = context;
context = DOMServices::getParentOfNode(*context);
}
assert(theNode != 0);
return theNode;
}
}
}
int
XPath::findPreceeding(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
opPos += 3;
// Ugh. Reverse document order, no parents, I guess.
const XalanNode::NodeType theType = context->getNodeType();
XalanNode* const topNode = findTopNode(context, theType);
XalanNode* pos = topNode;
// If the context node is an attribute, we need to perform some
// magic to stop the search at the appropriate point, which is when
// we arrive back at the parent.
const bool contextIsAttribute =
theType == XalanNode::ATTRIBUTE_NODE ? true : false;
const XalanNode* const theAttributeContextParent =
contextIsAttribute == true ? DOMServices::getParentOfNode(*context) : 0;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
while(0 != pos)
{
if(context == pos)
{
break;
}
const eMatchScore score =
theTester(*pos, pos->getNodeType());
assert(score == nodeTest(executionContext, pos, pos->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
// Ugh. If I could think a little better tonight, I'm
// sure there's a better way to check for the parent.
bool isParent = false;
XalanNode* parent = DOMServices::getParentOfNode(*context);
while(0 != parent)
{
if(parent == pos)
{
isParent = true;
break;
}
parent = DOMServices::getParentOfNode(*parent);
}
if(isParent == false)
{
subQueryResults.addNode(pos);
}
}
XalanNode* nextNode = 0;
// Check to see if we're back at the attribute context node's
// parent, in which case, we should stop.
if (contextIsAttribute == true &&
pos == theAttributeContextParent)
{
nextNode = context;
}
else
{
nextNode = pos->getFirstChild();
}
while(0 == nextNode)
{
nextNode = pos->getNextSibling();
if(0 == nextNode)
{
pos = DOMServices::getParentOfNode(*pos);
if(topNode == pos)
{
nextNode = 0;
break;
}
}
}
pos = nextNode;
}
// Now, reverse the order of the nodes, since
// preceeding is a reverse axis, and we searched
// the document from the root to this node.
subQueryResults.reverse();
subQueryResults.setReverseDocumentOrder();
return argLen + 3;
}
int
XPath::findPreceedingSiblings(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
XalanNode* pos = context->getPreviousSibling();
if (pos != 0)
{
opPos += 3;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const eMatchScore score =
theTester(*pos, pos->getNodeType());
assert(score == nodeTest(executionContext, pos, pos->getNodeType(), opPos, argLen, stepType));
if(eMatchScoreNone != score)
{
subQueryResults.addNode(pos);
}
pos = pos->getPreviousSibling();
} while(0 != pos);
subQueryResults.setReverseDocumentOrder();
}
return argLen + 3;
}
int
XPath::findNamespace(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int stepType,
MutableNodeRefList& subQueryResults) const
{
assert(context != 0);
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
if(context->getNodeType() == XalanNode::ELEMENT_NODE)
{
opPos += 3;
// Look up the element chain until we hit the document, so that we
// get all of the attribute/namespace nodes.
const XalanNode* const theOwnerDocument = context->getOwnerDocument();
assert(theOwnerDocument != 0);
const XalanNode* theCurrentNode = context;
const NodeTester theTester(
*this,
executionContext,
opPos,
argLen,
stepType);
do
{
const XalanNamedNodeMap* const attributeList =
theCurrentNode->getAttributes();
if(attributeList != 0)
{
const unsigned int nAttrs = attributeList->getLength();
for(unsigned int i = 0; i < nAttrs; ++i)
{
XalanNode* const attr = attributeList->item(i);
assert(attr != 0 && attr->getNodeType() == XalanNode::ATTRIBUTE_NODE);
const XalanDOMString& theNodeName = attr->getNodeName();
// This is an optimization to keep non-namespace attributes out of
// the call to nodeTest().
if (startsWith(theNodeName, DOMServices::s_XMLNamespaceWithSeparator) == true ||
::equals(theNodeName, DOMServices::s_XMLNamespace) == true)
{
const eMatchScore score =
theTester(*attr, XalanNode::ATTRIBUTE_NODE);
assert(score == nodeTest(executionContext, attr, XalanNode::ATTRIBUTE_NODE, opPos, argLen, stepType));
if(score != eMatchScoreNone)
{
subQueryResults.addNode(attr);
}
}
}
}
theCurrentNode = theCurrentNode->getParentNode();
} while (theCurrentNode != theOwnerDocument && theCurrentNode != 0);
}
return argLen + 3;
}
int
XPath::findNodesOnUnknownAxis(
XPathExecutionContext& executionContext,
XalanNode* context,
int opPos,
int /* stepType */,
MutableNodeRefList& /* subQueryResults */) const
{
const XPathExpression& currentExpression = getExpression();
// $$ ToDO: Can we reduce this to some call on the
// XPathExpression interface?
const int argLen =
currentExpression.getOpCodeMapValue(opPos + XPathExpression::s_opCodeMapLengthIndex + 1) - 3;
executionContext.error(TranscodeFromLocalCodePage("Unknown axis!"), context, getLocator());
return argLen + 3;
}
XPath::eMatchScore
XPath::nodeTest(
XPathExecutionContext& executionContext,
XalanNode* context,
XalanNode::NodeType nodeType,
int opPos,
int argLen,
int stepType) const
{
assert(context->getNodeType() == nodeType);
const XPathExpression& currentExpression = getExpression();
eMatchScore score = eMatchScoreNone;
const int testType = currentExpression.getOpCodeMapValue(opPos);
switch(testType)
{
case XPathExpression::eNODETYPE_COMMENT:
if (XalanNode::COMMENT_NODE == nodeType)
{
score = eMatchScoreNodeTest;
}
break;
case XPathExpression::eNODETYPE_TEXT:
if ((XalanNode::CDATA_SECTION_NODE == nodeType ||
XalanNode::TEXT_NODE == nodeType) &&
executionContext.shouldStripSourceNode(*context) == false)
{
score = eMatchScoreNodeTest;
}
break;
case XPathExpression::eNODETYPE_PI:
if(XalanNode::PROCESSING_INSTRUCTION_NODE == nodeType)
{
if(argLen == 1)
{
score = eMatchScoreNodeTest;
}
else if(argLen == 2)
{
opPos++;
const int tokenPosition =
currentExpression.getOpCodeMapValue(opPos);
const XObject* const name =
currentExpression.getToken(tokenPosition);
assert(name != 0);
if (::equals(context->getNodeName(), name->str()) == true)
{
score = eMatchScoreQName;
}
}
else
{
executionContext.error(TranscodeFromLocalCodePage("Arg length of processing-instruction() node test is incorrect!"), context, getLocator());
}
}
break;
case XPathExpression::eNODETYPE_NODE:
if (nodeType == XalanNode::CDATA_SECTION_NODE ||
nodeType == XalanNode::TEXT_NODE)
{
if (executionContext.shouldStripSourceNode(*context) == false)
{
score = eMatchScoreNodeTest;
}
}
else
{
score = eMatchScoreNodeTest;
}
break;
case XPathExpression::eNODETYPE_ROOT:
if (XalanNode::DOCUMENT_FRAGMENT_NODE == nodeType ||
XalanNode::DOCUMENT_NODE == nodeType)
{
score = eMatchScoreOther;
}
break;
case XPathExpression::eNODENAME:
{
opPos++;
if (nodeType == XalanNode::ATTRIBUTE_NODE || nodeType == XalanNode::ELEMENT_NODE)
{
bool test = false;
int queueIndex = currentExpression.getOpCodeMapValue(opPos);
const XalanDOMString& targetNS = queueIndex >= 0 ?
currentExpression.getToken(queueIndex)->str() :
s_emptyString;
opPos++;
// From the draft: "Two expanded names are equal if they
// have the same local part, and either both have no URI or
// both have the same URI."
// "A node test * is true for any node of the principal node type.
// For example, child::* will select all element children of the
// context node, and attribute::* will select all attributes of
// the context node."
// "A node test can have the form NCName:*. In this case, the prefix
// is expanded in the same way as with a QName using the context
// namespace declarations. The node test will be true for any node
// of the principal type whose expanded name has the URI to which
// the prefix expands, regardless of the local part of the name."
const bool isTotallyWild =
0 == length(targetNS) &&
currentExpression.getOpCodeMapValue(opPos) == XPathExpression::eELEMWILDCARD;
bool didMatchNS = false;
if(isTotallyWild == false)
{
const XalanDOMString& contextNS = DOMServices::getNamespaceOfNode(*context);
if(0 != length(targetNS) && 0 != length(contextNS))
{
test = ::equals(contextNS, targetNS);
didMatchNS = true;
}
else
{
test = XPathExpression::eELEMWILDCARD == queueIndex ||
(0 == length(contextNS) && 0 == length(targetNS));
}
}
else
{
test = true;
}
queueIndex = currentExpression.getOpCodeMapValue(opPos);
if(test == true)
{
switch(nodeType)
{
case XalanNode::ATTRIBUTE_NODE:
if(stepType == XPathExpression::eFROM_ATTRIBUTES ||
stepType == XPathExpression::eFROM_NAMESPACE)
{
assert(context->getNodeType() == XalanNode::ATTRIBUTE_NODE);
const XalanDOMString& attrName =
context->getNodeName();
const bool isNamespace =
startsWith(attrName, DOMServices::s_XMLNamespaceWithSeparator) ||
::equals(attrName, DOMServices::s_XMLNamespace);
if(XPathExpression::eELEMWILDCARD == queueIndex)
{
if(stepType == XPathExpression::eFROM_ATTRIBUTES)
{
if (isNamespace == false)
{
score = eMatchScoreNodeTest;
}
}
else
{
if (isNamespace == true)
{
score = eMatchScoreNodeTest;
}
}
}
else
{
if(stepType == XPathExpression::eFROM_ATTRIBUTES)
{
if (isNamespace == false)
{
assert(queueIndex >= 0);
const XalanDOMString& targetLocalName =
currentExpression.getToken(queueIndex)->str();
const XalanDOMString& localAttrName =
DOMServices::getLocalNameOfNode(*context);
if (::equals(localAttrName, targetLocalName) == true)
{
score = eMatchScoreQName;
}
}
}
else
{
if (isNamespace == true)
{
const XalanAttr* const theAttrNode =
#if defined(XALAN_OLD_STYLE_CASTS)
(const XalanAttr*)context;
#else
static_cast<const XalanAttr*>(context);
#endif
assert(theAttrNode != 0);
const XalanDOMString& theNamespace =
theAttrNode->getValue();
assert(queueIndex >= 0);
const XalanDOMString& targetLocalName =
currentExpression.getToken(queueIndex)->str();
if (::equals(theNamespace, targetLocalName) == true)
{
score = eMatchScoreQName;
}
}
}
}
}
break;
case XalanNode::ELEMENT_NODE:
if(stepType != XPathExpression::eFROM_ATTRIBUTES)
{
if(XPathExpression::eELEMWILDCARD == queueIndex)
{
score = didMatchNS == true ?
eMatchScoreNSWild : eMatchScoreNodeTest;
}
else
{
assert(queueIndex >= 0);
const XalanDOMString& targetLocalName =
currentExpression.getToken(queueIndex)->str();
if (::equals(DOMServices::getLocalNameOfNode(*context),
targetLocalName) == true)
{
score = eMatchScoreQName;
}
}
}
break;
default:
// Trying to match on anything else causes nasty bugs.
break;
} // end switch(nodeType)
} // end if(test)
} // end if (nodeType == XalanNode::ATTRIBUTE_NODE || nodeType == XalanNode::ELEMENT_NODE)
} // end case XPathExpression::eNODENAME
break;
default:
break;
} // end switch(testType)
return score;
}
void
XPath::predicates(
XPathExecutionContext& executionContext,
XalanNode* /* context */,
int opPos,
MutableNodeRefList& subQueryResults,
int& endPredicatesPos) const
{
const XPathExpression& currentExpression = getExpression();
int nextStepType =
currentExpression.getOpCodeMapValue(opPos);
while(XPathExpression::eOP_PREDICATE == nextStepType)
{
const NodeRefListBase::size_type theLength = subQueryResults.getLength();
// If we have no nodes left, then there's no point in executing any
// predicates. However, we will continue to loop, since we need to
// update endPredicatePos.
if (theLength > 0)
{
const int predOpPos = opPos + 2;
// OK, this is a huge hack/optimization. If the predicate is
// simple a number, such as [2], we can just get the
// numeric value from the expression, and not bother executing
// the predicate. Furthermore, we don't need to execute the
// predicate for each node, since the evaluation is no dependent
// on the context node. All we really have to do is remove all
// nodes from subQueryResults, _except_ for the node at that
// position. The only trick is that XPath indexes from 1, while
// our node lists index from 0.
if (m_expression.getOpCodeMapValue(predOpPos) == XPathExpression::eOP_NUMBERLIT)
{
assert(m_expression.m_tokenQueue.size() > unsigned(m_expression.m_opMap[predOpPos + 3]));
// Get the value of the number...
const double theIndex =
m_expression.getNumberLiteral(m_expression.getOpCodeMapValue(predOpPos + 2));
// If the index is out of range, or not an integer, just clear subQueryResults...
if (theIndex <= 0.0 ||
NodeRefListBase::size_type(theIndex) > theLength ||
double(NodeRefListBase::size_type(theIndex)) != theIndex)
{
subQueryResults.clear();
}
else if (theLength > 1)
{
// Save the matching node...
XalanNode* const theNode =
subQueryResults.item(NodeRefListBase::size_type(theIndex) - 1);
// Clear the list...
subQueryResults.clear();
// Add the node back in...
subQueryResults.addNode(theNode);
}
else
{
// OK, if there's only 1 node in the list, then
// we don't need to bother modifying the list.
// Just assert that theIndex == 1.0...
assert(theIndex == 1.0);
}
}
else
{
for(NodeRefListBase::size_type i = 0; i < theLength; ++i)
{
XalanNode* const theNode = subQueryResults.item(i);
assert(theNode != 0);
const XObjectPtr pred(predicate(theNode, opPos, executionContext));
assert(pred.get() != 0);
// Remove any node that doesn't satisfy the predicate.
if((XObject::eTypeNumber == pred->getType() && i + 1 != pred->num()) ||
pred->boolean() == false)
{
// Set the node to 0. After we're done,
// we'll clear it out.
subQueryResults.setNode(i, 0);
}
}
// Clear out any null entries...
subQueryResults.clearNulls();
}
}
opPos = currentExpression.getNextOpCodePosition(opPos);
nextStepType = currentExpression.getOpCodeMapValue(opPos);
if(XPathExpression::eOP_PREDICATE == nextStepType)
{
executionContext.setContextNodeList(subQueryResults);
}
}
endPredicatesPos = opPos;
}
inline const XalanDOMString*
getStringFromTokenQueue(
const XPathExpression& expression,
int opPos)
{
const int tokenPosition =
expression.getOpCodeMapValue(opPos);
if (tokenPosition < 0)
{
return 0;
}
else
{
const XObject* const token =
expression.getToken(tokenPosition);
assert(token != 0);
return &token->str();
}
}
XPath::NodeTester::NodeTester(
const XPath& xpath,
XPathExecutionContext& executionContext,
int opPos,
int argLen,
int stepType) :
m_executionContext(executionContext),
m_targetNamespace(0),
m_targetLocalName(0),
m_testFunction(0)
{
const XPathExpression& theExpression = xpath.getExpression();
switch(theExpression.getOpCodeMapValue(opPos))
{
case XPathExpression::eNODETYPE_COMMENT:
m_testFunction = &NodeTester::testComment;
break;
case XPathExpression::eNODETYPE_TEXT:
m_testFunction = &NodeTester::testText;
break;
case XPathExpression::eNODETYPE_PI:
if (argLen == 1)
{
m_testFunction = &NodeTester::testPI;
}
else if(argLen == 2)
{
m_testFunction = &NodeTester::testPIName;
m_targetLocalName = getStringFromTokenQueue(
theExpression,
opPos + 1);
}
else
{
executionContext.error(TranscodeFromLocalCodePage("Arg length of processing-instruction() node test is incorrect!"), 0, xpath.getLocator());
}
break;
case XPathExpression::eNODETYPE_NODE:
m_testFunction = &NodeTester::testNode;
break;
case XPathExpression::eNODETYPE_ROOT:
m_testFunction = &NodeTester::testRoot;
break;
case XPathExpression::eNODENAME:
{
bool isTotallyWild = false;
m_targetNamespace = getStringFromTokenQueue(
theExpression,
opPos + 1);
if (m_targetNamespace == 0 &&
theExpression.getOpCodeMapValue(opPos + 2) == XPathExpression::eELEMWILDCARD)
{
isTotallyWild = true;
}
else
{
m_targetLocalName = getStringFromTokenQueue(
theExpression,
opPos + 2);
}
if(stepType == XPathExpression::eFROM_ATTRIBUTES)
{
if (isTotallyWild == true)
{
m_testFunction = &NodeTester::testAttributeTotallyWild;
}
else if (m_targetNamespace == 0)
{
assert(m_targetLocalName != 0);
m_testFunction = &NodeTester::testAttributeNCName;
}
else if (m_targetLocalName == 0)
{
assert(m_targetNamespace != 0);
m_testFunction = &NodeTester::testAttributeNamespaceOnly;
}
else
{
assert(m_targetNamespace != 0 && m_targetLocalName != 0);
m_testFunction = &NodeTester::testAttributeQName;
}
}
else if (stepType == XPathExpression::eFROM_NAMESPACE)
{
if (isTotallyWild == true)
{
m_testFunction = &NodeTester::testNamespaceTotallyWild;
}
else
{
m_testFunction = &NodeTester::testNamespaceNCName;
}
}
else
{
if (isTotallyWild == true)
{
m_testFunction = &NodeTester::testElementTotallyWild;
}
else if (m_targetNamespace == 0)
{
m_testFunction = &NodeTester::testElementNCName;
}
else if (m_targetLocalName == 0)
{
assert(m_targetNamespace != 0);
m_testFunction = &NodeTester::testElementNamespaceOnly;
}
else
{
assert(m_targetNamespace != 0 && m_targetLocalName != 0);
m_testFunction = &NodeTester::testElementQName;
}
}
}
break;
default:
m_testFunction = &NodeTester::testDefault;
break;
}
assert(m_testFunction != 0);
}
XPath::eMatchScore
XPath::NodeTester::testComment(
const XalanNode& /* context */,
XalanNode::NodeType nodeType) const
{
if (XalanNode::COMMENT_NODE == nodeType)
{
return eMatchScoreNodeTest;
}
else
{
return eMatchScoreNone;
}
}
XPath::eMatchScore
XPath::NodeTester::testText(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
if ((XalanNode::CDATA_SECTION_NODE == nodeType || XalanNode::TEXT_NODE == nodeType) &&
shouldStripSourceNode(context) == false)
{
return eMatchScoreNodeTest;
}
else
{
return eMatchScoreNone;
}
}
XPath::eMatchScore
XPath::NodeTester::testPI(
const XalanNode& /* context */,
XalanNode::NodeType nodeType) const
{
if (XalanNode::PROCESSING_INSTRUCTION_NODE == nodeType)
{
return eMatchScoreNodeTest;
}
else
{
return eMatchScoreNone;
}
}
XPath::eMatchScore
XPath::NodeTester::testPIName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetLocalName != 0);
if (XalanNode::PROCESSING_INSTRUCTION_NODE == nodeType &&
::equals(context.getNodeName(), *m_targetLocalName) == true)
{
return eMatchScoreQName;
}
else
{
return eMatchScoreNone;
}
}
XPath::eMatchScore
XPath::NodeTester::testNode(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
if ((nodeType != XalanNode::CDATA_SECTION_NODE && nodeType != XalanNode::TEXT_NODE) ||
shouldStripSourceNode(context) == false)
{
return eMatchScoreNodeTest;
}
else
{
return eMatchScoreNone;
}
}
XPath::eMatchScore
XPath::NodeTester::testRoot(
const XalanNode& /* context */,
XalanNode::NodeType nodeType) const
{
if (XalanNode::DOCUMENT_FRAGMENT_NODE == nodeType ||
XalanNode::DOCUMENT_NODE == nodeType)
{
return eMatchScoreOther;
}
else
{
return eMatchScoreNone;
}
}
inline bool
isNamespaceDeclaration(const XalanNode& theAttributeNode)
{
assert(theAttributeNode.getNodeType() == XalanNode::ATTRIBUTE_NODE);
#if defined(XALAN_OLD_STYLE_CASTS)
return DOMServices::isNamespaceDeclaration((const XalanAttr&)theAttributeNode);
#else
return DOMServices::isNamespaceDeclaration(static_cast<const XalanAttr&>(theAttributeNode));
#endif
}
XPath::eMatchScore
XPath::NodeTester::testAttributeNCName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace == 0 && m_targetLocalName != 0);
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == true ||
matchLocalName(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreQName;
}
}
XPath::eMatchScore
XPath::NodeTester::testAttributeQName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace != 0 && m_targetLocalName != 0);
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == true ||
matchLocalNameAndNamespaceURI(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreQName;
}
}
XPath::eMatchScore
XPath::NodeTester::testAttributeNamespaceOnly(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace != 0 && m_targetLocalName == 0);
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == true ||
matchNamespaceURI(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreNodeTest;
}
}
XPath::eMatchScore
XPath::NodeTester::testAttributeTotallyWild(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == true)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreNodeTest;
}
}
XPath::eMatchScore
XPath::NodeTester::testElementNCName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace == 0 && m_targetLocalName != 0);
if (XalanNode::ELEMENT_NODE != nodeType ||
matchLocalName(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreQName;
}
}
XPath::eMatchScore
XPath::NodeTester::testElementQName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace != 0 && m_targetLocalName != 0);
if (XalanNode::ELEMENT_NODE != nodeType ||
matchLocalNameAndNamespaceURI(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreQName;
}
}
XPath::eMatchScore
XPath::NodeTester::testElementNamespaceOnly(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace != 0 && m_targetLocalName == 0);
if (XalanNode::ELEMENT_NODE != nodeType ||
matchNamespaceURI(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreNSWild;
}
}
XPath::eMatchScore
XPath::NodeTester::testElementTotallyWild(
const XalanNode& /* context */,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace == 0 && m_targetLocalName == 0);
if (XalanNode::ELEMENT_NODE != nodeType)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreNodeTest;
}
}
XPath::eMatchScore
XPath::NodeTester::testNamespaceNCName(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace == 0 && m_targetLocalName != 0);
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == false ||
matchNamespace(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreQName;
}
}
XPath::eMatchScore
XPath::NodeTester::testNamespaceTotallyWild(
const XalanNode& context,
XalanNode::NodeType nodeType) const
{
assert(m_targetNamespace == 0 && m_targetLocalName == 0);
if (XalanNode::ATTRIBUTE_NODE != nodeType ||
isNamespaceDeclaration(context) == false)
{
return eMatchScoreNone;
}
else
{
return eMatchScoreNodeTest;
}
}
XPath::eMatchScore
XPath::NodeTester::testDefault(
const XalanNode& /* context */,
XalanNode::NodeType /* nodeType */) const
{
return eMatchScoreNone;
}
bool
XPath::NodeTester::matchLocalName(const XalanNode& context) const
{
assert(m_targetLocalName != 0);
return length(context.getNamespaceURI()) == 0 &&
::equals(DOMServices::getLocalNameOfNode(context), *m_targetLocalName);
}
bool
XPath::NodeTester::matchNamespaceURI(const XalanNode& context) const
{
assert(m_targetNamespace != 0);
return ::equals(context.getNamespaceURI(), *m_targetNamespace);
}
bool
XPath::NodeTester::matchLocalNameAndNamespaceURI(const XalanNode& context) const
{
assert(m_targetNamespace != 0 && m_targetLocalName != 0);
return ::equals(DOMServices::getLocalNameOfNode(context), *m_targetLocalName) == true &&
::equals(context.getNamespaceURI(), *m_targetNamespace) == true;
}
bool
XPath::NodeTester::matchNamespace(const XalanNode& context) const
{
assert(m_targetLocalName != 0);
return ::equals(context.getNodeValue(), *m_targetLocalName);
}
bool
XPath::NodeTester::shouldStripSourceNode(const XalanNode& context) const
{
assert(context.getNodeType() == XalanNode::CDATA_SECTION_NODE ||
context.getNodeType() == XalanNode::TEXT_NODE);
return m_executionContext.shouldStripSourceNode(context);
}
static XalanDOMString PSEUDONAME_ANY;
static XalanDOMString PSEUDONAME_ROOT;
static XalanDOMString PSEUDONAME_TEXT;
static XalanDOMString PSEUDONAME_COMMENT;
static XalanDOMString PSEUDONAME_PI;
static XalanDOMString PSEUDONAME_OTHER;
static XalanDOMString PSEUDONAME_NODE;
const XalanDOMString& XPath::PSEUDONAME_ANY = ::PSEUDONAME_ANY;
const XalanDOMString& XPath::PSEUDONAME_ROOT = ::PSEUDONAME_ROOT;
const XalanDOMString& XPath::PSEUDONAME_TEXT = ::PSEUDONAME_TEXT;
const XalanDOMString& XPath::PSEUDONAME_COMMENT = ::PSEUDONAME_COMMENT;
const XalanDOMString& XPath::PSEUDONAME_PI = ::PSEUDONAME_PI;
const XalanDOMString& XPath::PSEUDONAME_OTHER = ::PSEUDONAME_OTHER;
const XalanDOMString& XPath::PSEUDONAME_NODE = ::PSEUDONAME_NODE;
// Don't auto-create the table...
XPath::FunctionTableType XPath::s_functions(false);
void
XPath::initialize()
{
s_functions.CreateTable();
::PSEUDONAME_ANY = XALAN_STATIC_UCODE_STRING("*");
::PSEUDONAME_ROOT = XALAN_STATIC_UCODE_STRING("/");
::PSEUDONAME_TEXT = XALAN_STATIC_UCODE_STRING("#text");
::PSEUDONAME_COMMENT = XALAN_STATIC_UCODE_STRING("#comment");
::PSEUDONAME_PI = XALAN_STATIC_UCODE_STRING("#pi");
::PSEUDONAME_OTHER = XALAN_STATIC_UCODE_STRING("*");
::PSEUDONAME_NODE = XALAN_STATIC_UCODE_STRING("#node");
}
void
XPath::terminate()
{
releaseMemory(::PSEUDONAME_ANY);
releaseMemory(::PSEUDONAME_ROOT);
releaseMemory(::PSEUDONAME_TEXT);
releaseMemory(::PSEUDONAME_COMMENT);
releaseMemory(::PSEUDONAME_PI);
releaseMemory(::PSEUDONAME_OTHER);
releaseMemory(::PSEUDONAME_NODE);
s_functions.DestroyTable();
}