blob: 32d6ee92ad04fbcbd88ee8053b6428c9926aae04 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-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 "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 "XPathProcessorImpl.hpp"
#include <sax/Locator.hpp>
#include <PlatformSupport/DOMStringHelper.hpp>
#include <PlatformSupport/DOMStringPrintWriter.hpp>
#include <PlatformSupport/DoubleSupport.hpp>
#include <PlatformSupport/PrefixResolver.hpp>
#include <PlatformSupport/XalanXMLChar.hpp>
#include <DOMSupport/DOMServices.hpp>
#include "XalanQName.hpp"
#include "XPathEnvSupport.hpp"
#include "XPathExecutionContext.hpp"
#include "XPathParserException.hpp"
XPathProcessorImpl::XPathProcessorImpl() :
m_token(),
m_tokenChar(0),
m_xpath(0),
m_expression(0),
m_prefixResolver(0),
m_requireLiterals(false),
m_positionPredicateStack()
{
}
XPathProcessorImpl::~XPathProcessorImpl()
{
}
void
XPathProcessorImpl::initXPath(
XPath& pathObj,
const XalanDOMString& expression,
const PrefixResolver& prefixResolver,
const Locator* locator)
{
m_requireLiterals = false;
m_xpath = &pathObj;
m_expression = &m_xpath->getExpression();
m_prefixResolver = &prefixResolver;
m_locator = locator;
m_expression->reset();
tokenize(expression);
m_expression->appendOpCode(XPathExpression::eOP_XPATH);
nextToken();
Expr();
if (length(m_token) != 0)
{
error("Extra illegal tokens!");
}
m_xpath = 0;
m_expression = 0;
m_prefixResolver = 0;
m_locator = 0;
m_positionPredicateStack.clear();
}
void
XPathProcessorImpl::initMatchPattern(
XPath& pathObj,
const XalanDOMString& expression,
const PrefixResolver& prefixResolver,
const Locator* locator)
{
m_xpath = &pathObj;
m_expression = &m_xpath->getExpression();
m_prefixResolver = &prefixResolver;
m_locator = locator;
m_expression->reset();
tokenize(expression);
m_expression->appendOpCode(XPathExpression::eOP_MATCHPATTERN);
nextToken();
// This is an optimization, but it's mostly a hacky
// bug fix. We don't handle match patterns with
// leading "//" correctly. Since the semantics
// of a match pattern with "//" are identical to
// those of one without "//", there's no point
// in even encoding them, so we just re-parse
// the XPath stripping off the leading "//".
// Parsing once _with_ the "//" ensures that
// we detect any errors with the unmodified
// match pattern.
bool fStripAndReparse = false;
if (tokenIs(XalanUnicode::charSolidus) == true &&
lookahead(XalanUnicode::charSolidus, 1) == true)
{
fStripAndReparse = true;
}
Pattern();
if (length(m_token) != 0)
{
error("Extra illegal tokens!");
}
if (fStripAndReparse == true)
{
initMatchPattern(
pathObj,
XalanDOMString(expression, 2, expression.length() - 2),
prefixResolver,
locator);
}
else
{
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->shrink();
m_xpath = 0;
m_expression = 0;
m_prefixResolver = 0;
m_locator = 0;
m_positionPredicateStack.clear();
}
}
void
XPathProcessorImpl::tokenize(
const XalanDOMString& pat,
DOMStringVectorType* targetStrings)
{
m_expression->setCurrentPattern(pat);
const int nChars = length(pat);
int startSubstring = -1;
int posOfNSSep = -1;
bool isStartOfPat = true;
bool isAttrName = false;
// Nesting of '[' so we can know if the given element should be
// counted inside the m_patternMap.
int nesting = 0;
XalanDOMString theToken;
for(int i = 0; i < nChars; i++)
{
XalanDOMChar c = charAt(pat, i);
switch(c)
{
case XalanUnicode::charQuoteMark:
{
if(startSubstring != -1)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
isAttrName = false;
if(-1 != posOfNSSep)
{
posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
}
else
{
substring(pat, theToken, startSubstring, i);
addToTokenQueue(theToken);
}
}
startSubstring = i;
for(++i; i < nChars && (c = charAt(pat, i)) != XalanUnicode::charQuoteMark; ++i);
if(c == XalanUnicode::charQuoteMark)
{
substring(pat, theToken, startSubstring, i + 1);
addToTokenQueue(theToken);
startSubstring = -1;
}
else
{
error("misquoted literal... expected double quote!");
}
}
break;
case XalanUnicode::charApostrophe:
{
if(startSubstring != -1)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
isAttrName = false;
if(-1 != posOfNSSep)
{
posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
}
else
{
substring(pat, theToken, startSubstring, i);
addToTokenQueue(theToken);
}
}
startSubstring = i;
for(++i; i < nChars && (c = charAt(pat, i)) != XalanUnicode::charApostrophe; ++i);
if(c == XalanUnicode::charApostrophe)
{
substring(pat, theToken, startSubstring, i + 1);
addToTokenQueue(theToken);
startSubstring = -1;
}
else
{
error("misquoted literal... expected single quote!");
}
}
break;
case XalanUnicode::charLF:
case XalanUnicode::charCR:
case XalanUnicode::charSpace:
case XalanUnicode::charHTab:
{
if(startSubstring != -1)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
isAttrName = false;
if(-1 != posOfNSSep)
{
posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
}
else
{
substring(pat, theToken, startSubstring, i);
addToTokenQueue(theToken);
}
startSubstring = -1;
}
}
break;
case XalanUnicode::charCommercialAt:
isAttrName = true;
// fall-through on purpose
case XalanUnicode::charHyphenMinus:
{
if(XalanUnicode::charHyphenMinus == c)
{
if(!(startSubstring == -1))
{
break;
}
}
}
// fall-through on purpose
case XalanUnicode::charLeftParenthesis:
case XalanUnicode::charLeftSquareBracket:
case XalanUnicode::charRightParenthesis:
case XalanUnicode::charRightSquareBracket:
case XalanUnicode::charVerticalLine:
case XalanUnicode::charSolidus:
case XalanUnicode::charAsterisk:
case XalanUnicode::charPlusSign:
case XalanUnicode::charEqualsSign:
case XalanUnicode::charComma:
case XalanUnicode::charReverseSolidus: // Unused at the moment
case XalanUnicode::charCircumflexAccent: // Unused at the moment
case XalanUnicode::charExclamationMark: // Unused at the moment
case XalanUnicode::charDollarSign:
case XalanUnicode::charLessThanSign:
case XalanUnicode::charGreaterThanSign:
{
if(startSubstring != -1)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
isAttrName = false;
if(-1 != posOfNSSep)
{
posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, i);
}
else
{
substring(pat, theToken, startSubstring, i);
addToTokenQueue(theToken);
}
startSubstring = -1;
}
else if(XalanUnicode::charSolidus == c && isStartOfPat == true)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
}
else if(XalanUnicode::charAsterisk == c)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
isAttrName = false;
}
if(0 == nesting)
{
if(XalanUnicode::charVerticalLine == c)
{
if(0 != targetStrings)
{
recordTokenString(*targetStrings);
}
isStartOfPat = true;
}
}
if(XalanUnicode::charRightParenthesis == c || XalanUnicode::charRightSquareBracket == c)
{
nesting--;
}
else if(XalanUnicode::charLeftParenthesis == c || XalanUnicode::charLeftSquareBracket == c)
{
nesting++;
}
substring(pat, theToken, i, i + 1);
addToTokenQueue(theToken);
}
break;
case XalanUnicode::charColon:
{
if(posOfNSSep == i - 1 && i > 0)
{
if(startSubstring != -1)
{
if (startSubstring < i - 1)
{
substring(pat, theToken, startSubstring, i - 1);
addToTokenQueue(theToken);
}
}
isAttrName = false;
startSubstring = -1;
posOfNSSep = -1;
substring(pat, theToken, i - 1, i + 1);
addToTokenQueue(theToken);
break;
}
else
{
posOfNSSep = i;
}
}
// fall through on purpose
default:
{
if(-1 == startSubstring)
{
startSubstring = i;
if (XalanXMLChar::isDigit(c) == true)
{
bool gotFullStop = false;
while(i < nChars - 1)
{
++i;
const XalanDOMChar currentChar = charAt(pat, i);
if (currentChar == XalanUnicode::charFullStop)
{
if (gotFullStop == false)
{
gotFullStop = true;
}
else
{
--i;
break;
}
}
else if (XalanXMLChar::isDigit(currentChar) == false)
{
--i;
break;
}
}
substring(pat, theToken, startSubstring, i + 1);
addToTokenQueue(theToken);
startSubstring = -1;
}
}
}
}
}
if(startSubstring != -1)
{
isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
if(-1 != posOfNSSep)
{
posOfNSSep = mapNSTokens(pat, startSubstring, posOfNSSep, nChars);
}
else
{
substring(pat, theToken, startSubstring, nChars);
addToTokenQueue(theToken);
}
}
if (0 == m_expression->tokenQueueSize())
{
error("Empty expression!");
}
else if (0 != targetStrings)
{
recordTokenString(*targetStrings);
}
m_expression->setTokenPosition(0);
}
bool
XPathProcessorImpl::mapPatternElemPos(
int nesting,
bool isStart,
bool isAttrName) const
{
if(0 == nesting)
{
if(!isStart)
{
m_expression->adjustPattern(m_expression->patternMapSize() - 1,
-TARGETEXTRA);
}
const int theValue =
m_expression->tokenQueueSize() - (isAttrName ? 1 : 0) + TARGETEXTRA;
m_expression->pushPattern(theValue);
isStart = false;
}
return isStart;
}
void
XPathProcessorImpl::recordTokenString(DOMStringVectorType& targetStrings)
{
assert(m_expression != 0);
int tokPos = getTokenQueuePosFromMap(m_expression->patternMapSize() - 1);
resetTokenMark(tokPos + 1);
if(lookahead(XalanUnicode::charLeftParenthesis, 1) == true)
{
const int tok = getKeywordToken(m_token);
switch(tok)
{
case XPathExpression::eNODETYPE_COMMENT:
targetStrings.push_back(XPath::PSEUDONAME_COMMENT);
break;
case XPathExpression::eNODETYPE_TEXT:
targetStrings.push_back(XPath::PSEUDONAME_TEXT);
break;
case XPathExpression::eNODETYPE_NODE:
targetStrings.push_back(XPath::PSEUDONAME_ANY);
break;
case XPathExpression::eNODETYPE_ROOT:
targetStrings.push_back(XPath::PSEUDONAME_ROOT);
break;
case XPathExpression::eNODETYPE_ANYELEMENT:
targetStrings.push_back(XPath::PSEUDONAME_ANY);
break;
case XPathExpression::eNODETYPE_PI:
targetStrings.push_back(XPath::PSEUDONAME_ANY);
break;
default:
targetStrings.push_back(XPath::PSEUDONAME_ANY);
break;
}
}
else
{
if(tokenIs(XalanUnicode::charCommercialAt) == true)
{
tokPos++;
resetTokenMark(tokPos + 1);
}
if(lookahead(XalanUnicode::charColon, 1) == true)
{
tokPos += 2;
}
assert(m_expression->getToken(tokPos) != 0);
targetStrings.push_back(m_expression->getToken(tokPos)->str());
}
}
void
XPathProcessorImpl::addToTokenQueue(const XalanDOMString& s) const
{
assert(m_xpath != 0);
assert(m_expression != 0);
m_expression->pushToken(s);
}
int
XPathProcessorImpl::mapNSTokens(
const XalanDOMString& pat,
int startSubstring,
int posOfNSSep,
int posOfScan) const
{
assert(m_prefixResolver != 0);
const XalanDOMString prefix(pat, startSubstring, posOfNSSep - startSubstring);
if (XalanQName::isValidNCName(prefix) == false)
{
error(XalanDOMString("'") + prefix + XalanDOMString("' is not a valid NCName"));
}
const XalanDOMString* const uName =
m_prefixResolver->getNamespaceForPrefix(prefix);
if(uName == 0)
{
error(
TranscodeFromLocalCodePage("Unable to resolve prefix '") +
prefix +
TranscodeFromLocalCodePage("'."));
}
else if (length(*uName) == 0)
{
error(
TranscodeFromLocalCodePage("The prefix '") +
prefix +
TranscodeFromLocalCodePage("' is bound to a zero-length URI."));
}
else
{
addToTokenQueue(*uName);
addToTokenQueue(DOMServices::s_XMLNamespaceSeparatorString);
// If there's no local part, then don't bother. We need to check
// this because '*' tokenizes separately, which means "ns:*" tokenizes
// differently from "ns:foo". In the first case, '*' will be tokenized
// _after_ this code, in the second 'ns:foo' will be split into tokens
// here...
if(posOfNSSep + 1 < posOfScan)
{
const XalanDOMString s(pat, posOfNSSep + 1, posOfScan - (posOfNSSep + 1));
assert(length(s) > 0);
if (XalanQName::isValidNCName(s) == false)
{
error(XalanDOMString("'") + s + XalanDOMString("' is not a valid NCName"));
}
else
{
addToTokenQueue(s);
}
}
}
return -1;
}
int
XPathProcessorImpl::getTokenQueuePosFromMap(int i) const
{
assert(m_expression != 0);
const int pos = m_expression->getPattern(i);
return pos >= TARGETEXTRA ? pos - TARGETEXTRA : pos;
}
bool
XPathProcessorImpl::tokenIs(const XalanDOMString& s) const
{
return equals(m_token, s);
}
bool
XPathProcessorImpl::tokenIs(const XalanDOMChar* s) const
{
return equals(m_token, s);
}
bool
XPathProcessorImpl::tokenIs(const char* s) const
{
const unsigned int theTokenLength = length(m_token);
const unsigned int theStringLength = XalanDOMString::length(s);
if (theTokenLength != theStringLength)
{
return false;
}
else
{
unsigned int i = 0;
while(i < theStringLength)
{
if (charAt(m_token, i ) != s[i])
{
break;
}
else
{
++i;
}
}
return i == theStringLength ? true : false;
}
}
bool
XPathProcessorImpl::tokenIs(char c) const
{
return m_tokenChar == c ? true : false;
}
bool
XPathProcessorImpl::lookahead(
XalanDOMChar c,
int n) const
{
const XalanDOMString& tok =
getTokenRelative(n - 1);
if (length(tok) == 1 &&
charAt(tok, 0) == c)
{
return true;
}
else
{
return false;
}
}
bool
XPathProcessorImpl::lookahead(
const XalanDOMChar* s,
int n) const
{
assert(s != 0);
const XalanDOMString& tok =
getTokenRelative(n - 1);
return equals(tok, s);
}
bool
XPathProcessorImpl::lookahead(
const XalanDOMString& s,
int n) const
{
const XalanDOMString& tok =
getTokenRelative(n - 1);
return equals(tok, s);
}
bool
XPathProcessorImpl::lookbehind(
char c,
int n) const
{
const XalanDOMString& tok =
getTokenRelative(-(n + 1));
if (length(tok) == 1 &&
charAt(tok, 0) == c)
{
return true;
}
else
{
return false;
}
}
bool
XPathProcessorImpl::lookbehindHasToken(int n) const
{
const XalanDOMString& tok =
getTokenRelative(-(n + 1));
const XalanDOMChar c0 = length(tok) == 0 ? XalanUnicode::charVerticalLine : charAt(tok, 0);
return c0 == XalanUnicode::charVerticalLine ? false : true;
}
void
XPathProcessorImpl::nextToken()
{
assert(m_expression != 0);
const XObject* const theNextToken =
m_expression->getNextToken();
if (theNextToken == 0)
{
clear(m_token);
}
else
{
m_token = theNextToken->str();
}
if(length(m_token) > 0)
{
m_tokenChar = charAt(m_token, 0);
}
else
{
m_tokenChar = 0;
}
}
void
XPathProcessorImpl::prevToken()
{
assert(m_expression != 0);
const XObject* const thePreviousToken =
m_expression->getPreviousToken();
m_token = thePreviousToken == 0 ? XalanDOMString() : thePreviousToken->str();
if(length(m_token) > 0)
{
m_tokenChar = charAt(m_token, 0);
}
else
{
m_tokenChar = 0;
}
}
const XalanDOMString&
XPathProcessorImpl::getTokenRelative(int theOffset) const
{
assert(m_expression != 0);
const XObject* const theToken =
m_expression->getRelativeToken(theOffset);
return theToken == 0 ? s_emptyString : theToken->str();
}
void
XPathProcessorImpl::resetTokenMark(int mark)
{
m_expression->setTokenPosition(mark);
nextToken();
}
void
XPathProcessorImpl::consumeExpected(const char* expected)
{
if(tokenIs(expected) == true)
{
nextToken();
}
else
{
error(TranscodeFromLocalCodePage("Expected ") +
TranscodeFromLocalCodePage(expected) +
TranscodeFromLocalCodePage(", but found: ") +
m_token);
}
}
void
XPathProcessorImpl::consumeExpected(char expected)
{
if(tokenIs(expected) == true)
{
nextToken();
}
else
{
XalanDOMString theMsg(TranscodeFromLocalCodePage("Expected "));
append(theMsg, expected);
append(theMsg, ", but found: ");
append(theMsg, m_token);
error(theMsg);
}
}
void
XPathProcessorImpl::error(
const XalanDOMString& msg,
XalanNode* /* sourceNode */) const
{
XalanDOMString emsg;
if (m_expression == 0)
{
emsg = msg;
}
else
{
const XalanDOMString& theCurrentPattern =
m_expression->getCurrentPattern();
DOMStringPrintWriter thePrintWriter(emsg);
thePrintWriter.print(msg);
thePrintWriter.println();
if (length(theCurrentPattern) != 0)
{
thePrintWriter.print(XALAN_STATIC_UCODE_STRING("pattern = '"));
thePrintWriter.print(theCurrentPattern);
thePrintWriter.print("'");
if (m_locator != 0)
{
const XalanDOMChar* const theSystemID =
m_locator->getSystemId();
thePrintWriter.print("(");
if (theSystemID == 0)
{
thePrintWriter.print("Unknown URI");
}
else
{
thePrintWriter.print(theSystemID);
}
thePrintWriter.print(", ");
thePrintWriter.print(m_locator->getLineNumber());
thePrintWriter.print(", ");
thePrintWriter.print(m_locator->getColumnNumber());
thePrintWriter.print(")");
}
thePrintWriter.println();
}
// Back up one token, since we've consumed one...
m_expression->getPreviousToken();
// Ask the expression to dump the remaining tokens...
m_expression->dumpRemainingTokenQueue(thePrintWriter);
}
if (m_locator != 0)
{
const XalanDOMChar* const theSystemID =
m_locator->getSystemId();
XalanDOMString theURI;
if (theSystemID != 0)
{
theURI = theSystemID;
}
throw XPathParserException(
emsg,
theURI,
m_locator->getLineNumber(),
m_locator->getColumnNumber());
}
else
{
throw XPathParserException(emsg);
}
}
void
XPathProcessorImpl::error(
const char* msg,
XalanNode* sourceNode) const
{
error(TranscodeFromLocalCodePage(msg), sourceNode);
}
int
XPathProcessorImpl::getKeywordToken(const XalanDOMString& key) const
{
KeywordsMapType::const_iterator i =
s_keywords.find(key);
if (i == s_keywords.end())
{
return 0;
}
else
{
return (*i).second;
}
}
int
XPathProcessorImpl::getFunctionToken(const XalanDOMString& key) const
{
FunctionNameMapType::const_iterator i = s_functions.find(key);
if (i != s_functions.end())
{
return (*i).second;
}
else
{
return 0;
}
}
void
XPathProcessorImpl::Expr()
{
OrExpr();
}
void
XPathProcessorImpl::OrExpr()
{
const int opPos = m_expression->opCodeMapLength();
AndExpr();
if(tokenIs(s_orString) == true)
{
nextToken();
m_expression->insertOpCode(XPathExpression::eOP_OR,
opPos);
OrExpr();
m_expression->updateOpCodeLength(XPathExpression::eOP_OR,
opPos);
}
}
void
XPathProcessorImpl::AndExpr()
{
const int opPos = m_expression->opCodeMapLength();
EqualityExpr();
if(tokenIs(s_andString) == true)
{
nextToken();
m_expression->insertOpCode(XPathExpression::eOP_AND,
opPos);
AndExpr();
m_expression->updateOpCodeLength(XPathExpression::eOP_AND,
opPos);
}
}
int
XPathProcessorImpl::EqualityExpr(int opCodePos)
{
int theOpDisplacement = 0;
const int opPos = opCodePos != -1 ? opCodePos : m_expression->opCodeMapLength();
RelationalExpr();
XPathExpression::eOpCodes theOpCode =
XPathExpression::eENDOP;
if(tokenIs(XalanUnicode::charExclamationMark) && lookahead(XalanUnicode::charEqualsSign, 1))
{
nextToken();
nextToken();
theOpCode = XPathExpression::eOP_NOTEQUALS;
}
else if(tokenIs(XalanUnicode::charEqualsSign))
{
nextToken();
theOpCode = XPathExpression::eOP_EQUALS;
}
if (theOpCode != XPathExpression::eENDOP)
{
// Save the number of bytes we inserted
// into the map.
const int theLocalDisplacement =
m_expression->insertOpCode(theOpCode,
opPos);
// Update the length
m_expression->updateOpCodeLength(theOpCode,
opPos);
// Do the right term of the expression.
theOpDisplacement += EqualityExpr(opPos);
// If there's any displacement from the right
// term, update the length for a shift. Otherwise,
// just update the length.
if (theOpDisplacement > 0)
{
m_expression->updateShiftedOpCodeLength(theOpCode,
opPos,
opPos + theOpDisplacement);
}
else
{
m_expression->updateOpCodeLength(theOpCode,
opPos);
}
// Accumulate the displacement.
theOpDisplacement += theLocalDisplacement;
}
return theOpDisplacement;
}
int
XPathProcessorImpl::RelationalExpr(int opCodePos)
{
int theOpDisplacement = 0;
const int opPos = opCodePos != -1 ? opCodePos : m_expression->opCodeMapLength();
AdditiveExpr();
if(0 != length(m_token))
{
XPathExpression::eOpCodes theOpCode =
XPathExpression::eENDOP;
if(tokenIs(XalanUnicode::charLessThanSign) == true)
{
nextToken();
if(tokenIs(XalanUnicode::charEqualsSign) == true)
{
nextToken();
theOpCode = XPathExpression::eOP_LTE;
}
else
{
theOpCode = XPathExpression::eOP_LT;
}
}
else if(tokenIs(XalanUnicode::charGreaterThanSign) == true)
{
nextToken();
if(tokenIs(XalanUnicode::charEqualsSign) == true)
{
nextToken();
theOpCode = XPathExpression::eOP_GTE;
}
else
{
theOpCode = XPathExpression::eOP_GT;
}
}
if (theOpCode != XPathExpression::eENDOP)
{
// Save the number of bytes we inserted
// into the map.
const int theLocalDisplacement =
m_expression->insertOpCode(theOpCode,
opPos);
// Update the length
m_expression->updateOpCodeLength(theOpCode,
opPos);
// Do the right term of the expression.
theOpDisplacement += RelationalExpr(opPos);
// If there's any displacement from the right
// term, update the length for a shift. Otherwise,
// just update the length.
if (theOpDisplacement > 0)
{
m_expression->updateShiftedOpCodeLength(theOpCode,
opPos,
opPos + theOpDisplacement);
}
else
{
m_expression->updateOpCodeLength(theOpCode,
opPos);
}
// Accumulate the displacement.
theOpDisplacement += theLocalDisplacement;
}
}
return theOpDisplacement;
}
int
XPathProcessorImpl::AdditiveExpr(int opCodePos)
{
int theOpDisplacement = 0;
const int opPos = opCodePos != -1 ? opCodePos : m_expression->opCodeMapLength();
MultiplicativeExpr();
if(0 != length(m_token))
{
XPathExpression::eOpCodes theOpCode =
XPathExpression::eENDOP;
if(tokenIs(XalanUnicode::charPlusSign) == true)
{
theOpCode = XPathExpression::eOP_PLUS;
}
else if(tokenIs(XalanUnicode::charHyphenMinus) == true)
{
theOpCode = XPathExpression::eOP_MINUS;
}
if (theOpCode != XPathExpression::eENDOP)
{
nextToken();
// Save the number of bytes we inserted
// into the map.
const int theLocalDisplacement =
m_expression->insertOpCode(theOpCode,
opPos);
// Update the length
m_expression->updateOpCodeLength(theOpCode,
opPos);
// Do the right term of the expression.
theOpDisplacement += AdditiveExpr(opPos);
// If there's any displacement from the right
// term, update the length for a shift. Otherwise,
// just update the length.
if (theOpDisplacement > 0)
{
m_expression->updateShiftedOpCodeLength(theOpCode,
opPos,
opPos + theOpDisplacement);
}
else
{
m_expression->updateOpCodeLength(theOpCode,
opPos);
}
// Accumulate the displacement.
theOpDisplacement += theLocalDisplacement;
}
}
return theOpDisplacement;
}
int
XPathProcessorImpl::MultiplicativeExpr(int opCodePos)
{
int theOpDisplacement = 0;
const int opPos = opCodePos != -1 ? opCodePos : m_expression->opCodeMapLength();
UnaryExpr();
if(0 != length(m_token))
{
XPathExpression::eOpCodes theOpCode =
XPathExpression::eENDOP;
if(tokenIs(XalanUnicode::charAsterisk) == true)
{
theOpCode = XPathExpression::eOP_MULT;
}
else if(tokenIs(s_divString) == true)
{
theOpCode = XPathExpression::eOP_DIV;
}
else if(tokenIs(s_modString) == true)
{
theOpCode = XPathExpression::eOP_MOD;
}
if (theOpCode != XPathExpression::eENDOP)
{
nextToken();
// Save the number of bytes we inserted
// into the map.
const int theLocalDisplacement =
m_expression->insertOpCode(theOpCode,
opPos);
// Update the length
m_expression->updateOpCodeLength(theOpCode,
opPos);
// Do the right term of the expression.
theOpDisplacement += MultiplicativeExpr(opPos);
// If there's any displacement from the right
// term, update the length for a shift. Otherwise,
// just update the length.
if (theOpDisplacement > 0)
{
m_expression->updateShiftedOpCodeLength(theOpCode,
opPos,
opPos + theOpDisplacement);
}
else
{
m_expression->updateOpCodeLength(theOpCode,
opPos);
}
// Accumulate the displacement.
theOpDisplacement += theLocalDisplacement;
}
}
return theOpDisplacement;
}
void
XPathProcessorImpl::UnaryExpr()
{
const int opPos = m_expression->opCodeMapLength();
bool isNeg = false;
if(tokenIs(XalanUnicode::charHyphenMinus) == true)
{
nextToken();
m_expression->insertOpCode(XPathExpression::eOP_NEG,
opPos);
isNeg = true;
}
UnionExpr();
if(isNeg == true)
{
m_expression->updateOpCodeLength(XPathExpression::eOP_NEG,
opPos);
}
}
void
XPathProcessorImpl::BooleanExpr()
{
const int opPos = m_expression->opCodeMapLength();
m_expression->appendOpCode(XPathExpression::eOP_BOOL);
Expr();
const int opLen = m_expression->opCodeMapLength() - opPos;
if(opLen == 2)
{
error("boolean(...) argument is no longer optional with 19990709 XPath draft.");
}
m_expression->updateOpCodeLength(XPathExpression::eOP_BOOL,
opPos);
}
void
XPathProcessorImpl::UnionExpr()
{
const int opPos = m_expression->opCodeMapLength();
bool continueOrLoop = true;
bool foundUnion = false;
do
{
PathExpr();
if(tokenIs(XalanUnicode::charVerticalLine) == true)
{
if(false == foundUnion)
{
foundUnion = true;
m_expression->insertOpCode(XPathExpression::eOP_UNION,
opPos);
}
nextToken();
}
else
{
if (foundUnion == true)
{
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
}
break;
}
}
while(continueOrLoop == true);
m_expression->updateOpCodeLength(opPos);
}
void
XPathProcessorImpl::PathExpr()
{
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
FilterExpr();
if(tokenIs(XalanUnicode::charSolidus) == true)
{
nextToken();
m_expression->insertOpCode(XPathExpression::eOP_LOCATIONPATH,
opPos);
RelativeLocationPath();
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(XPathExpression::eOP_LOCATIONPATH,
opPos);
}
}
void
XPathProcessorImpl::FilterExpr()
{
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
// const bool isFunc = lookahead(XalanUnicode::charLeftParenthesis, 1);
PrimaryExpr();
if(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
m_expression->insertOpCode(XPathExpression::eOP_LOCATIONPATH,
opPos);
while(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
Predicate();
}
if(tokenIs(XalanUnicode::charSolidus) == true)
{
nextToken();
RelativeLocationPath();
}
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(XPathExpression::eOP_LOCATIONPATH,
opPos);
}
}
void
XPathProcessorImpl::PrimaryExpr()
{
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
if(tokenIs(XalanUnicode::charApostrophe) == true ||
tokenIs(XalanUnicode::charQuoteMark) == true)
{
m_expression->appendOpCode(XPathExpression::eOP_LITERAL);
Literal();
m_expression->updateOpCodeLength(XPathExpression::eOP_LITERAL,
opPos);
}
else if(tokenIs(XalanUnicode::charDollarSign) == true)
{
nextToken(); // consume '$'
m_expression->appendOpCode(XPathExpression::eOP_VARIABLE);
QName();
m_expression->updateOpCodeLength(XPathExpression::eOP_VARIABLE,
opPos);
}
else if(tokenIs(XalanUnicode::charLeftParenthesis) == true)
{
nextToken();
m_expression->appendOpCode(XPathExpression::eOP_GROUP);
Expr();
consumeExpected(XalanUnicode::charRightParenthesis);
m_expression->updateOpCodeLength(XPathExpression::eOP_GROUP,
opPos);
}
else if((tokenIs(XalanUnicode::charFullStop) == true &&
length(m_token) > 1 &&
XalanXMLChar::isDigit(charAt(m_token, 1)) == true) ||
XalanXMLChar::isDigit(m_tokenChar) == true)
{
m_expression->appendOpCode(XPathExpression::eOP_NUMBERLIT);
Number();
m_expression->updateOpCodeLength(XPathExpression::eOP_NUMBERLIT,
opPos);
}
else if(lookahead(XalanUnicode::charLeftParenthesis, 1) == true ||
(lookahead(XalanUnicode::charColon, 1) == true && lookahead(XalanUnicode::charLeftParenthesis, 3) == true))
{
FunctionCall();
}
else
{
LocationPath();
}
}
void
XPathProcessorImpl::Argument()
{
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
m_expression->appendOpCode(XPathExpression::eOP_ARGUMENT);
if (m_requireLiterals == false ||
isCurrentLiteral() == true)
{
Expr();
}
else
{
error(TranscodeFromLocalCodePage("A literal argument is required!"));
}
m_expression->updateOpCodeLength(XPathExpression::eOP_ARGUMENT,
opPos);
}
int
XPathProcessorImpl::FunctionCallArguments()
{
int argCount = 0;
consumeExpected(XalanUnicode::charLeftParenthesis);
while(tokenIs(XalanUnicode::charRightParenthesis) == false && isEmpty(m_token) == false)
{
if(tokenIs(XalanUnicode::charComma) == true)
{
error("Found ',' but no preceding argument!");
}
Argument();
++argCount;
if(tokenIs(XalanUnicode::charRightParenthesis) == false)
{
consumeExpected(XalanUnicode::charComma);
if(tokenIs(XalanUnicode::charRightParenthesis) == true)
{
error("Found ',' but no following argument!");
}
}
}
consumeExpected(XalanUnicode::charRightParenthesis);
return argCount;
}
void
XPathProcessorImpl::FunctionCall()
{
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
if(lookahead(XalanUnicode::charColon, 1) == true)
{
m_expression->appendOpCode(XPathExpression::eOP_EXTFUNCTION);
XPathExpression::OpCodeMapValueVectorType theArgs(2, 0);
theArgs[0] = m_expression->getTokenPosition() - 1;
nextToken();
consumeExpected(XalanUnicode::charColon);
theArgs[1] = m_expression->getTokenPosition() - 1;
m_expression->setOpCodeArgs(XPathExpression::eOP_EXTFUNCTION,
opPos,
theArgs);
nextToken();
FunctionCallArguments();
}
else
{
if (isValidFunction(m_token) == false)
{
error(TranscodeFromLocalCodePage("Could not find function: ") +
m_token +
TranscodeFromLocalCodePage("()"));
}
const int funcTok = getFunctionToken(m_token);
switch(funcTok)
{
case XPathExpression::eNODETYPE_PI:
case XPathExpression::eNODETYPE_COMMENT:
case XPathExpression::eNODETYPE_TEXT:
case XPathExpression::eNODETYPE_NODE:
LocationPath();
// ************** Nasty return here!!! *********************** //
return;
break;
default:
{
// The position must be at least zero, since
// we've looked at a token.
assert(m_expression->getTokenPosition() > 0);
int theFunctionID =
XPath::getFunctionTable().nameToID(m_token);
// This code is disabled for the time being, as
// it needs more testing.
#if 0
if (equals(m_token, s_positionString) == true &&
m_positionPredicateStack.empty() == false)
{
m_positionPredicateStack.back() = true;
}
#endif
XPathExpression::OpCodeMapValueVectorType theArgs(2, 0);
theArgs[0] = theFunctionID;
theArgs[1] = 0;
m_expression->appendOpCode(XPathExpression::eOP_FUNCTION,
theArgs);
}
}
nextToken();
// Get the arguments, and the argument count...
const int argCount = FunctionCallArguments();
assert(m_expression->m_opMap[opPos + 3] == 0);
// update the arg count in the op map...
m_expression->m_opMap[opPos + 3] = argCount;
}
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(opPos);
}
void
XPathProcessorImpl::LocationPath()
{
const int opPos = m_expression->opCodeMapLength();
m_expression->appendOpCode(XPathExpression::eOP_LOCATIONPATH);
if(tokenIs(XalanUnicode::charSolidus) == true)
{
const int newOpPos = m_expression->opCodeMapLength();
// Tell how long the step is without the predicate
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
m_expression->appendOpCode(XPathExpression::eFROM_ROOT,
theArgs);
m_expression->appendOpCode(XPathExpression::eNODETYPE_ROOT);
// Tell how long the entire step is.
m_expression->updateOpCodeLength(newOpPos);
nextToken();
}
if(length(m_token) != 0)
{
RelativeLocationPath();
}
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(XPathExpression::eOP_LOCATIONPATH,
opPos);
}
void
XPathProcessorImpl::RelativeLocationPath()
{
Step();
while(tokenIs(XalanUnicode::charSolidus) == true)
{
nextToken();
Step();
}
}
void
XPathProcessorImpl::Step()
{
const int opPos = m_expression->opCodeMapLength();
if(tokenIs(s_dotString) == true)
{
nextToken();
if(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
error("'..[predicate]' or '.[predicate]' is illegal syntax. Use 'self::node()[predicate]' instead.");
}
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
m_expression->appendOpCode(XPathExpression::eFROM_SELF,
theArgs);
m_expression->appendOpCode(XPathExpression::eNODETYPE_NODE);
// Tell how long the entire step is.
m_expression->updateOpCodeLength(opPos);
}
else if(tokenIs(s_dotDotString) == true)
{
nextToken();
// Tell how long the step is without the predicate
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
m_expression->appendOpCode(XPathExpression::eFROM_PARENT,
theArgs);
m_expression->appendOpCode(XPathExpression::eNODETYPE_NODE);
// Tell how long the entire step is.
m_expression->updateOpCodeLength(opPos);
}
else if (tokenIs(XalanUnicode::charAsterisk) ||
tokenIs(XalanUnicode::charCommercialAt) ||
tokenIs(XalanUnicode::charSolidus) ||
(tokenIs(XalanUnicode::charLowLine) ||
XalanXMLChar::isLetter(charAt(m_token, 0))))
{
Basis();
while(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
Predicate();
}
// Tell how long the entire step is.
m_expression->updateOpCodeLength(opPos);
}
else if (tokenIs(XalanUnicode::charRightParenthesis) == false)
{
error("Unexpected token!");
}
}
void
XPathProcessorImpl::Basis()
{
assert(m_xpath != 0);
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
int axisType = 0;
// The next blocks guarantee that a FROM_XXX will be added.
if(lookahead(s_axisString, 1) == true)
{
axisType = AxisName();
nextToken();
nextToken();
}
else if(tokenIs(XalanUnicode::charCommercialAt) == true)
{
axisType = XPathExpression::eFROM_ATTRIBUTES;
m_expression->appendOpCode(XPathExpression::eFROM_ATTRIBUTES);
nextToken();
}
else if(tokenIs(XalanUnicode::charSolidus) == true)
{
// Check the current token in the expression. It's
// actually the next token in this context.
//
const XalanDOMString& theNextToken = getTokenRelative(0);
if (isAxis(theNextToken) == false && isNodeTest(theNextToken) == false)
{
nextToken();
error("Expected axis or node test!");
}
else
{
// Tell how long the step is without the predicate
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
m_expression->appendOpCode(XPathExpression::eFROM_DESCENDANTS_OR_SELF,
theArgs);
m_expression->appendOpCode(XPathExpression::eNODETYPE_NODE);
// Tell how long the step is without the predicate
m_expression->updateOpCodeLengthAfterNodeTest(opPos);
return; // make a quick exit...
}
}
else
{
axisType = XPathExpression::eFROM_CHILDREN;
m_expression->appendOpCode(XPathExpression::eFROM_CHILDREN);
}
NodeTest(axisType);
// Tell how long the step is without the predicate
m_expression->updateOpCodeLengthAfterNodeTest(opPos);
}
int
XPathProcessorImpl::AxisName()
{
assert(m_xpath != 0);
assert(m_expression != 0);
const AxisNamesMapType::const_iterator i =
s_axisNames.find(m_token);
if (i == s_axisNames.end())
{
error(TranscodeFromLocalCodePage("illegal axis name: ") +
m_token);
}
else
{
m_expression->appendOpCode((*i).second);
}
return (*i).second;
}
void
XPathProcessorImpl::NodeTest(int axisType)
{
assert(m_xpath != 0);
assert(m_expression != 0);
if(lookahead(XalanUnicode::charLeftParenthesis, 1) == true)
{
NodeTypesMapType::const_iterator i =
s_nodeTypes.find(m_token);
if (i == s_nodeTypes.end())
{
error(TranscodeFromLocalCodePage("Unknown nodetype: ") +
m_token);
}
else
{
nextToken();
m_expression->appendOpCode((*i).second);
consumeExpected(XalanUnicode::charLeftParenthesis);
if(XPathExpression::eNODETYPE_PI == (*i).second)
{
if(tokenIs(XalanUnicode::charRightParenthesis) == false)
{
Literal();
}
}
consumeExpected(XalanUnicode::charRightParenthesis);
}
}
else
{
// Assume name of attribute or element.
m_expression->appendOpCode(XPathExpression::eNODENAME);
if(lookahead(XalanUnicode::charColon, 1) == true)
{
if(tokenIs(XalanUnicode::charAsterisk) == true)
{
m_expression->appendOpCode(XPathExpression::eELEMWILDCARD);
}
else
{
m_expression->pushCurrentTokenOnOpCodeMap();
}
nextToken();
consumeExpected(XalanUnicode::charColon);
}
else
{
m_expression->appendOpCode(XPathExpression::eEMPTY);
}
if(tokenIs(XalanUnicode::charAsterisk) == true)
{
m_expression->appendOpCode(XPathExpression::eELEMWILDCARD);
}
else if (isNodeTest(m_token) == false)
{
error("Expected node test!");
}
else
{
if (axisType == XPathExpression::eFROM_NAMESPACE)
{
const XObject* const theToken =
m_expression->getRelativeToken(-1);
assert(theToken != 0);
const XalanDOMString& theString = theToken->str();
const XalanDOMString* const theNamespace =
m_prefixResolver->getNamespaceForPrefix(theString);
if (theNamespace != 0)
{
m_expression->replaceRelativeToken(
-1,
*theNamespace);
}
}
m_expression->pushCurrentTokenOnOpCodeMap();
}
nextToken();
}
}
void
XPathProcessorImpl::Predicate()
{
if(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
nextToken();
PredicateExpr();
consumeExpected(XalanUnicode::charRightSquareBracket);
}
}
void
XPathProcessorImpl::PredicateExpr()
{
assert(m_xpath != 0);
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
m_expression->appendOpCode(XPathExpression::eOP_PREDICATE);
m_positionPredicateStack.push_back(false);
Expr();
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(XPathExpression::eOP_PREDICATE,
opPos);
assert(m_positionPredicateStack.empty() == false);
if (m_positionPredicateStack.back() == true)
{
m_expression->replaceOpCode(
opPos,
XPathExpression::eOP_PREDICATE,
XPathExpression::eOP_PREDICATE_WITH_POSITION);
}
m_positionPredicateStack.pop_back();
}
void
XPathProcessorImpl::QName()
{
assert(m_xpath != 0);
assert(m_expression != 0);
// If there is no prefix, we have to fake things out...
if (lookahead(XalanUnicode::charColon, 1) == false)
{
m_expression->insertToken(XalanDOMString());
m_expression->pushCurrentTokenOnOpCodeMap();
nextToken();
}
else
{
m_expression->pushCurrentTokenOnOpCodeMap();
nextToken();
consumeExpected(XalanUnicode::charColon);
}
m_expression->pushCurrentTokenOnOpCodeMap();
nextToken();
}
void
XPathProcessorImpl::NCName()
{
assert(m_xpath != 0);
assert(m_expression != 0);
m_expression->pushCurrentTokenOnOpCodeMap();
nextToken();
}
void
XPathProcessorImpl::Literal()
{
assert(m_xpath != 0);
assert(m_expression != 0);
if(isCurrentLiteral() == true)
{
const XalanDOMString theArgument(m_token, 1, length(m_token) - 2);
m_expression->pushArgumentOnOpCodeMap(theArgument);
nextToken();
}
else
{
error(TranscodeFromLocalCodePage("Pattern literal (") +
m_token +
TranscodeFromLocalCodePage(") needs to be quoted!"));
}
}
void
XPathProcessorImpl::Number()
{
assert(m_xpath != 0);
assert(m_expression != 0);
if(0 != length(m_token))
{
const double num = DoubleSupport::toDouble(m_token);
m_expression->pushNumberLiteralOnOpCodeMap(num);
m_expression->pushArgumentOnOpCodeMap(num);
nextToken();
}
}
void
XPathProcessorImpl::Pattern()
{
while(true)
{
LocationPathPattern();
if(tokenIs(XalanUnicode::charVerticalLine) == true)
{
nextToken();
}
else
{
break;
}
}
}
void
XPathProcessorImpl::LocationPathPattern()
{
assert(m_xpath != 0);
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
m_expression->appendOpCode(XPathExpression::eOP_LOCATIONPATHPATTERN);
if(lookahead(XalanUnicode::charLeftParenthesis, 1) == true &&
(tokenIs(s_functionIDString) == true ||
tokenIs(s_functionKeyString) == true))
{
IdKeyPattern();
if(tokenIs(XalanUnicode::charSolidus) == true && lookahead(XalanUnicode::charSolidus, 1) == true)
{
const int newOpPos = m_expression->opCodeMapLength();
// Tell how long the step is without the predicate
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
m_expression->appendOpCode(XPathExpression::eMATCH_ANY_ANCESTOR_WITH_FUNCTION_CALL,
theArgs);
m_expression->updateOpCodeLength(newOpPos);
nextToken();
}
}
else if(tokenIs(XalanUnicode::charSolidus) == true)
{
const int newOpPos = m_expression->opCodeMapLength();
// Tell how long the step is without the predicate
const XPathExpression::OpCodeMapValueVectorType theArgs(1, 4);
if(lookahead(XalanUnicode::charSolidus, 1) == true)
{
m_expression->appendOpCode(XPathExpression::eMATCH_ANY_ANCESTOR_WITH_PREDICATE,
theArgs);
}
else
{
m_expression->appendOpCode(XPathExpression::eFROM_ROOT,
theArgs);
}
m_expression->appendOpCode(XPathExpression::eNODETYPE_ROOT);
m_expression->updateOpCodeLength(newOpPos);
nextToken();
}
if(!tokenIs(XalanUnicode::charVerticalLine) == true && length(m_token) != 0)
{
RelativePathPattern();
}
// Terminate for safety.
m_expression->appendOpCode(XPathExpression::eENDOP);
m_expression->updateOpCodeLength(XPathExpression::eOP_LOCATIONPATHPATTERN,
opPos);
}
void
XPathProcessorImpl::IdKeyPattern()
{
m_requireLiterals = true;
FunctionCall();
m_requireLiterals = false;
}
void
XPathProcessorImpl::RelativePathPattern()
{
StepPattern();
while(tokenIs(XalanUnicode::charSolidus) == true)
{
nextToken();
StepPattern();
}
}
void
XPathProcessorImpl::StepPattern()
{
AbbreviatedNodeTestStep();
}
void
XPathProcessorImpl::AbbreviatedNodeTestStep()
{
assert(m_xpath != 0);
assert(m_expression != 0);
const int opPos = m_expression->opCodeMapLength();
int axisType = 0;
int matchTypePos = -1;
// The next blocks guarantee that a MATCH_XXX will be added.
if(tokenIs(XalanUnicode::charCommercialAt) == true)
{
axisType = XPathExpression::eMATCH_ATTRIBUTE;
m_expression->appendOpCode(XPathExpression::eMATCH_ATTRIBUTE);
nextToken();
}
else if(lookahead(s_axisString, 1) == true)
{
// $$$ To Do: Perhaps these strings should be in the
// axis table?
if(tokenIs(s_attributeString) == true)
{
axisType = XPathExpression::eMATCH_ATTRIBUTE;
m_expression->appendOpCode(XPathExpression::eMATCH_ATTRIBUTE);
}
else if(tokenIs(s_childString) == true)
{
axisType = XPathExpression::eMATCH_IMMEDIATE_ANCESTOR;
m_expression->appendOpCode(XPathExpression::eMATCH_IMMEDIATE_ANCESTOR);
}
else
{
error("Only child:: and attribute:: axes are allowed in match patterns!");
}
nextToken();
nextToken();
}
else if(tokenIs(XalanUnicode::charSolidus) == true)
{
axisType = XPathExpression::eMATCH_ANY_ANCESTOR;
m_expression->appendOpCode(XPathExpression::eMATCH_ANY_ANCESTOR);
nextToken();
}
else
{
if(tokenIs(XalanUnicode::charSolidus) == true)
{
nextToken();
}
matchTypePos = m_expression->opCodeMapLength();
axisType = XPathExpression::eMATCH_IMMEDIATE_ANCESTOR;
m_expression->appendOpCode(XPathExpression::eMATCH_IMMEDIATE_ANCESTOR);
}
// Make room for telling how long the step is without the predicate.
// This will be replaced by the right value.
m_expression->appendOpCode(XPathExpression::eENDOP);
NodeTest(axisType);
m_expression->updateOpCodeLengthAfterNodeTest(opPos);
while(tokenIs(XalanUnicode::charLeftSquareBracket) == true)
{
Predicate();
}
if(matchTypePos > -1 && tokenIs(XalanUnicode::charSolidus) == true && lookahead(XalanUnicode::charSolidus, 1) == true)
{
assert(m_expression->opCodeMapLength() > matchTypePos);
m_expression->m_opMap[matchTypePos] = XPathExpression::eMATCH_ANY_ANCESTOR;
}
m_expression->updateOpCodeLength(opPos);
}
bool
XPathProcessorImpl::isValidFunction(const XalanDOMString& key) const
{
bool fResult = true;
if(XPath::isInstalledFunction(key) == false)
{
if (getFunctionToken(key) == 0)
{
fResult = false;
}
}
return fResult;
}
bool
XPathProcessorImpl::isCurrentLiteral() const
{
const int last = length(m_token) - 1;
if (last <= 0)
{
return false;
}
else
{
assert(last > 0);
const XalanDOMChar c0 = m_tokenChar;
const XalanDOMChar cX = charAt(m_token, last);
if((c0 == XalanUnicode::charQuoteMark && cX == XalanUnicode::charQuoteMark) ||
(c0 == XalanUnicode::charApostrophe && cX == XalanUnicode::charApostrophe))
{
return true;
}
else
{
return false;
}
}
}
bool
XPathProcessorImpl::isAxis(const XalanDOMString& theToken)
{
const XalanDOMString::size_type theLength = length(theToken);
if (theLength == 0)
{
return false;
}
else if (theLength == 1 &&
charAt(theToken, 0) == XalanUnicode::charCommercialAt)
{
return true;
}
else
{
const AxisNamesMapType::const_iterator i =
s_axisNames.find(theToken);
if (i != s_axisNames.end())
{
return true;
}
else
{
return false;
}
}
}
bool
XPathProcessorImpl::isNodeTest(const XalanDOMString& theToken)
{
const XalanDOMString::size_type theLength = length(theToken);
if (theLength == 0)
{
return false;
}
else if (theLength == 1 &&
charAt(theToken, 0) == XalanUnicode::charAsterisk)
{
return true;
}
else if (charAt(theToken, 0) == XalanUnicode::charLowLine ||
XalanXMLChar::isLetter(charAt(theToken, 0)) == true)
{
return true;
}
else
{
return false;
}
}
void
XPathProcessorImpl::initializeKeywordsTable(KeywordsMapType& /* theKeywords */)
{
// $$$ ToDo: This is very confusing. This table is only used
// by getKeywordToken(). But if you look at the switch
// statement there, none of these values are considered in the
// case statement. So what's the point?
// theKeywords[FROM_SELF_ABBREVIATED_STRING] = XPathExpression::eFROM_SELF;
// theKeywords[FROM_ATTRIBUTE_STRING] = XPathExpression::eFROM_ATTRIBUTE;
// theKeywords[FROM_DOC_STRING] = XPathExpression::eFROM_DOC;
// theKeywords[FROM_DOCREF_STRING] = XPathExpression::eFROM_DOCREF;
// theKeywords[FROM_ID_STRING] = XPathExpression::eFROM_ID;
// theKeywords[FROM_IDREF_STRING] = XPathExpression::eFROM_IDREF;
// theKeywords[FUNC_ID_STRING] = XPathExpression::eFUNC_ID;
// theKeywords[FUNC_KEY_STRING] = XPathExpression::eFUNC_KEY;
// theKeywords[FUNC_DOCUMENT_STRING] = XPathExpression::eFUNC_DOC;
}
void
XPathProcessorImpl::initializeFunctionTable(FunctionNameMapType& theFunctions)
{
theFunctions[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("processing-instruction"))] = XPathExpression::eNODETYPE_PI;
theFunctions[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("comment"))] = XPathExpression::eNODETYPE_COMMENT;
theFunctions[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("text"))] = XPathExpression::eNODETYPE_TEXT;
theFunctions[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("node"))] = XPathExpression::eNODETYPE_NODE;
}
void
XPathProcessorImpl::initializeAxisNamesTable(AxisNamesMapType& theAxisNames)
{
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("ancestor"))] = XPathExpression::eFROM_ANCESTORS;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("ancestor-or-self"))] = XPathExpression::eFROM_ANCESTORS_OR_SELF;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("attribute"))] = XPathExpression::eFROM_ATTRIBUTES;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("child"))] = XPathExpression::eFROM_CHILDREN;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("descendant"))] = XPathExpression::eFROM_DESCENDANTS;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("descendant-or-self"))] = XPathExpression::eFROM_DESCENDANTS_OR_SELF;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("following"))] = XPathExpression::eFROM_FOLLOWING;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("following-sibling"))] = XPathExpression::eFROM_FOLLOWING_SIBLINGS;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("parent"))] = XPathExpression::eFROM_PARENT;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("preceding"))] = XPathExpression::eFROM_PRECEDING;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("preceding-sibling"))] = XPathExpression::eFROM_PRECEDING_SIBLINGS;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("self"))] = XPathExpression::eFROM_SELF;
theAxisNames[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("namespace"))] = XPathExpression::eFROM_NAMESPACE;
}
void
XPathProcessorImpl::initializeNodeTypesTable(NodeTypesMapType& theNodeTypes)
{
theNodeTypes[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("comment"))] = XPathExpression::eNODETYPE_COMMENT;
theNodeTypes[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("text"))] = XPathExpression::eNODETYPE_TEXT;
theNodeTypes[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("processing-instruction"))] = XPathExpression::eNODETYPE_PI;
theNodeTypes[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("node"))] = XPathExpression::eNODETYPE_NODE;
theNodeTypes[StaticStringToDOMString(XALAN_STATIC_UCODE_STRING("*"))] = XPathExpression::eNODETYPE_ANYELEMENT;
}
static XalanDOMString s_functionIDString;
static XalanDOMString s_functionKeyString;
static XalanDOMString s_orString;
static XalanDOMString s_andString;
static XalanDOMString s_divString;
static XalanDOMString s_modString;
static XalanDOMString s_dotString;
static XalanDOMString s_dotDotString;
static XalanDOMString s_axisString;
static XalanDOMString s_attributeString;
static XalanDOMString s_childString;
static XalanDOMString s_positionString;
const XalanDOMString XPathProcessorImpl::s_emptyString;
const XalanDOMString& XPathProcessorImpl::s_functionIDString = ::s_functionIDString;
// This shouldn't really be here, since it's not part of the XPath standard,
// but rather a part ofthe XSLT standard.
const XalanDOMString& XPathProcessorImpl::s_functionKeyString = ::s_functionKeyString;
const XalanDOMString& XPathProcessorImpl::s_orString = ::s_orString;
const XalanDOMString& XPathProcessorImpl::s_andString = ::s_andString;
const XalanDOMString& XPathProcessorImpl::s_divString = ::s_divString;
const XalanDOMString& XPathProcessorImpl::s_modString = ::s_modString;
const XalanDOMString& XPathProcessorImpl::s_dotString = ::s_dotString;
const XalanDOMString& XPathProcessorImpl::s_dotDotString = ::s_dotDotString;
const XalanDOMString& XPathProcessorImpl::s_axisString = ::s_axisString;
const XalanDOMString& XPathProcessorImpl::s_attributeString = ::s_attributeString;
const XalanDOMString& XPathProcessorImpl::s_childString = ::s_childString;
const XalanDOMString& XPathProcessorImpl::s_positionString = ::s_positionString;
static XPathProcessorImpl::KeywordsMapType s_keywords;
static XPathProcessorImpl::FunctionNameMapType s_functions;
static XPathProcessorImpl::AxisNamesMapType s_axisNames;
static XPathProcessorImpl::NodeTypesMapType s_nodeTypes;
const XPathProcessorImpl::KeywordsMapType& XPathProcessorImpl::s_keywords = ::s_keywords;
const XPathProcessorImpl::FunctionNameMapType& XPathProcessorImpl::s_functions = ::s_functions;
const XPathProcessorImpl::AxisNamesMapType& XPathProcessorImpl::s_axisNames = ::s_axisNames;
const XPathProcessorImpl::NodeTypesMapType& XPathProcessorImpl::s_nodeTypes = ::s_nodeTypes;
void
XPathProcessorImpl::initialize()
{
initializeKeywordsTable(::s_keywords);
initializeFunctionTable(::s_functions);
initializeAxisNamesTable(::s_axisNames);
initializeNodeTypesTable(::s_nodeTypes);
::s_functionIDString = XALAN_STATIC_UCODE_STRING("id");
::s_functionKeyString = XALAN_STATIC_UCODE_STRING("key");
::s_orString = XALAN_STATIC_UCODE_STRING("or");
::s_andString = XALAN_STATIC_UCODE_STRING("and");
::s_divString = XALAN_STATIC_UCODE_STRING("div");
::s_modString = XALAN_STATIC_UCODE_STRING("mod");
::s_dotString = XALAN_STATIC_UCODE_STRING(".");
::s_dotDotString = XALAN_STATIC_UCODE_STRING("..");
::s_axisString = XALAN_STATIC_UCODE_STRING("::");
::s_attributeString = XALAN_STATIC_UCODE_STRING("attribute");
::s_childString = XALAN_STATIC_UCODE_STRING("child");
::s_positionString = XALAN_STATIC_UCODE_STRING("position");
}
void
XPathProcessorImpl::terminate()
{
KeywordsMapType().swap(::s_keywords);
FunctionNameMapType().swap(::s_functions);
AxisNamesMapType().swap(::s_axisNames);
NodeTypesMapType().swap(::s_nodeTypes);
releaseMemory(::s_functionIDString);
releaseMemory(::s_functionKeyString);
releaseMemory(::s_orString);
releaseMemory(::s_andString);
releaseMemory(::s_divString);
releaseMemory(::s_modString);
releaseMemory(::s_dotString);
releaseMemory(::s_dotDotString);
releaseMemory(::s_axisString);
releaseMemory(::s_attributeString);
releaseMemory(::s_childString);
releaseMemory(::s_positionString);
}