/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: XPathParser.java,v 1.2.4.1 2005/09/14 19:46:02 jeffsuttor Exp $
*/
/**
* Tokenizes and parses XPath expressions. This should really be named
* XPathParserImpl, and may be renamed in the future.
* @xsl.usage general
*/
public class XPathParser
{
// %REVIEW% Is there a better way of doing this?
// Upside is minimum object churn. Downside is that we don't have a useful
// backtrace in the exception itself -- but we don't expect to need one.
/**
* The XPath to be processed.
*/
/**
* The next token in the pattern.
*/
/**
* The first char in m_token, the theory being that this
* is an optimization because we won't have to do charAt(0) as
* often.
*/
/**
* The position in the token queue is tracked by m_queueMark.
*/
/**
* Results from checking FilterExpr syntax
*/
/**
* The parser constructor.
*/
{
}
/**
* The prefix resolver to map prefixes to namespaces in the OpMap.
*/
/**
* Given an string, init an XPath object for selections,
* in order that a parse doesn't
* have to be done each time the expression is evaluated.
*
* @param compiler The compiler object.
* @param expression A string conforming to the XPath grammar.
* @param namespaceContext An object that is able to resolve prefixes in
* the XPath to namespaces.
*
* @throws javax.xml.transform.TransformerException
*/
public void initXPath(
{
// Patch for Christine's gripe. She wants her errorHandler to return from
// a fatal error and continue trying to parse, rather than throwing an exception.
// Without the patch, that put us into an endless loop.
//
// %REVIEW% Is there a better way of doing this?
// %REVIEW% Are there any other cases which need the safety net?
// (and if so do we care right now, or should we rewrite the XPath
// grammar engine and can fix it at that time?)
try {
nextToken();
Expr();
{
{
nextToken();
extraTokens += ", ";
}
}
}
{
{
// What I _want_ to do is null out this XPath.
// I doubt this has the desired effect, but I'm not sure what else to do.
// %REVIEW%!!!
}
else
throw e;
}
}
/**
* Given an string, init an XPath object for pattern matches,
* in order that a parse doesn't
* have to be done each time the expression is evaluated.
* @param compiler The XPath object to be initialized.
* @param expression A String representing the XPath.
* @param namespaceContext An object that is able to resolve prefixes in
* the XPath to namespaces.
*
* @throws javax.xml.transform.TransformerException
*/
public void initMatchPattern(
{
nextToken();
Pattern();
{
{
nextToken();
extraTokens += ", ";
}
}
// Terminate for safety.
}
/** The error listener where syntax errors are to be sent.
*/
/** The source location of the XPath. */
/** The table contains build-in functions and customized functions */
/**
* Allow an application to register an error event handler, where syntax
* errors will be sent. If the error listener is not set, syntax errors
* will be sent to System.err.
*
* @param handler Reference to error listener where syntax errors will be
* sent.
*/
{
}
/**
* Return the current error listener.
*
* @return The error listener, which should not normally be null, but may be.
*/
{
return m_errorListener;
}
/**
* Check whether m_token matches the target string.
*
* @param s A string reference or null.
*
* @return If m_token is null, returns false (or true if s is also null), or
* return true if the current token matches the string, else false.
*/
{
}
/**
* Check whether m_tokenChar==c.
*
* @param c A character to be tested.
*
* @return If m_token is null, returns false, or return true if c matches
* the current token.
*/
final boolean tokenIs(char c)
{
}
/**
* Look ahead of the current token in order to
* make a branching decision.
*
* @param c the character to be tested for.
* @param n number of tokens to look ahead. Must be
* greater than 1.
*
* @return true if the next token matches the character argument.
*/
final boolean lookahead(char c, int n)
{
int pos = (m_queueMark + n);
boolean b;
{
}
else
{
b = false;
}
return b;
}
/**
* Look behind the first character of the current token in order to
* make a branching decision.
*
* @param c the character to compare it to.
* @param n number of tokens to look behind. Must be
* greater than 1. Note that the look behind terminates
* at either the beginning of the string or on a '|'
* character. Because of this, this method should only
* be used for pattern matching.
*
* @return true if the token behind the current token matches the character
* argument.
*/
private final boolean lookbehind(char c, int n)
{
boolean isToken;
if (lookBehindPos >= 0)
{
{
}
else
{
isToken = false;
}
}
else
{
isToken = false;
}
return isToken;
}
/**
* look behind the current token in order to
* see if there is a useable token.
*
* @param n number of tokens to look behind. Must be
* greater than 1. Note that the look behind terminates
* at either the beginning of the string or on a '|'
* character. Because of this, this method should only
* be used for pattern matching.
*
* @return true if look behind has a token, false otherwise.
*/
private final boolean lookbehindHasToken(int n)
{
boolean hasToken;
if ((m_queueMark - n) > 0)
{
}
else
{
hasToken = false;
}
return hasToken;
}
/**
* Look ahead of the current token in order to
* make a branching decision.
*
* @param s the string to compare it to.
* @param n number of tokens to lookahead. Must be
* greater than 1.
*
* @return true if the token behind the current token matches the string
* argument.
*/
{
boolean isToken;
{
}
else
{
}
return isToken;
}
/**
* Retrieve the next token from the command and
* store it in m_token string.
*/
private final void nextToken()
{
{
}
else
{
m_tokenChar = 0;
}
}
/**
* Retrieve a token relative to the current token.
*
* @param i Position relative to current token.
*
* @return The string at the given index, or null if the index is out
* of range.
*/
{
int relative = m_queueMark + i;
{
}
else
{
}
return tok;
}
/**
* Retrieve the previous token from the command and
* store it in m_token string.
*/
private final void prevToken()
{
if (m_queueMark > 0)
{
m_queueMark--;
}
else
{
m_tokenChar = 0;
}
}
/**
* Consume an expected token, throwing an exception if it
* isn't there.
*
* @param expected The string to be expected.
*
* @throws javax.xml.transform.TransformerException
*/
{
{
nextToken();
}
else
{
m_token }); //"Expected "+expected+", but found: "+m_token);
// Patch for Christina's gripe. She wants her errorHandler to return from
// this error and continue trying to parse, rather than throwing an exception.
// Without the patch, that put us into an endless loop.
throw new XPathProcessorException(CONTINUE_AFTER_FATAL_ERROR);
}
}
/**
* Consume an expected token, throwing an exception if it
* isn't there.
*
* @param expected the character to be expected.
*
* @throws javax.xml.transform.TransformerException
*/
{
{
nextToken();
}
else
{
m_token }); //"Expected "+expected+", but found: "+m_token);
// Patch for Christina's gripe. She wants her errorHandler to return from
// this error and continue trying to parse, rather than throwing an exception.
// Without the patch, that put us into an endless loop.
throw new XPathProcessorException(CONTINUE_AFTER_FATAL_ERROR);
}
}
/**
* Warn the user of a problem.
*
* @param msg An error msgkey that corresponds to one of the constants found
* in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
* a key for a format string.
* @param args An array of arguments represented in the format string, which
* may be null.
*
* @throws TransformerException if the current ErrorListoner determines to
* throw an exception.
*/
{
{
// TO DO: Need to get stylesheet Locator from here.
}
else
{
// Should never happen.
}
}
/**
* Notify the user of an assertion error, and probably throw an
* exception.
*
* @param b If false, a runtime exception will be thrown.
* @param msg The assertion message, which should be informative.
*
* @throws RuntimeException if the b argument is false.
*/
{
if (!b)
{
throw new RuntimeException(fMsg);
}
}
/**
* Notify the user of an error, and probably throw an
* exception.
*
* @param msg An error msgkey that corresponds to one of the constants found
* in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
* a key for a format string.
* @param args An array of arguments represented in the format string, which
* may be null.
*
* @throws TransformerException if the current ErrorListoner determines to
* throw an exception.
*/
{
{
// TO DO: Need to get stylesheet Locator from here.
}
else
{
// System.err.println(fmsg);
throw te;
}
}
/**
* This method is added to support DOM 3 XPath API.
* <p>
* This method is exactly like error(String, Object[]); except that
* the underlying TransformerException is
* XpathStylesheetDOM3Exception (which extends TransformerException).
* <p>
* So older XPath code in Xalan is not affected by this. To older XPath code
* the behavior of whether error() or errorForDOM3() is called because it is
* always catching TransformerException objects and is oblivious to
* the new subclass of XPathStylesheetDOM3Exception. Older XPath code
* runs as before.
* <p>
* However, newer DOM3 XPath code upon catching a TransformerException can
* can check if the exception is an instance of XPathStylesheetDOM3Exception
* and take appropriate action.
*
* @param msg An error msgkey that corresponds to one of the constants found
* in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
* a key for a format string.
* @param args An array of arguments represented in the format string, which
* may be null.
*
* @throws TransformerException if the current ErrorListoner determines to
* throw an exception.
*/
{
{
// TO DO: Need to get stylesheet Locator from here.
}
else
{
// System.err.println(fmsg);
throw te;
}
}
/**
* Dump the remaining token queue.
* Thanks to Craig for this.
*
* @return A dump of the remaining token queue, which may be appended to
* an error message.
*/
{
int q = m_queueMark;
if (q < m_ops.getTokenQueueSize())
{
while (q < m_ops.getTokenQueueSize())
{
}
}
else
{
returnMsg = "";
}
return returnMsg;
}
/**
* Given a string, return the corresponding function token.
*
* @param key A local name of a function.
*
* @return The function ID, which may correspond to one of the FUNC_XXX
* values found in {@link com.sun.org.apache.xpath.internal.compiler.FunctionTable}, but may
* be a value installed by an external module.
*/
{
int tok;
try
{
// These are nodetests, xpathparser treats them as functions when parsing
// a FilterExpr.
}
catch (NullPointerException npe)
{
tok = -1;
}
catch (ClassCastException cce)
{
tok = -1;
}
return tok;
}
/**
* Insert room for operation. This will NOT set
* the length value of the operation, but will update
* the length value for the total expression.
*
* @param pos The position where the op is to be inserted.
* @param length The length of the operation space in the op map.
* @param op The op code to the inserted.
*/
{
{
}
}
/**
* Insert room for operation. This WILL set
* the length value of the operation, and will update
* the length value for the total expression.
*
* @param length The length of the operation.
* @param op The op code to the inserted.
*/
{
}
// ============= EXPRESSIONS FUNCTIONS =================
/**
*
*
* Expr ::= OrExpr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
OrExpr();
}
/**
*
*
* OrExpr ::= AndExpr
* | OrExpr 'or' AndExpr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
AndExpr();
{
nextToken();
OrExpr();
}
}
/**
*
*
* AndExpr ::= EqualityExpr
* | AndExpr 'and' EqualityExpr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
EqualityExpr(-1);
{
nextToken();
AndExpr();
}
}
/**
*
* @returns an Object which is either a String, a Number, a Boolean, or a vector
* of nodes.
*
* EqualityExpr ::= RelationalExpr
* | EqualityExpr '=' RelationalExpr
*
*
* @param addPos Position where expression is to be added, or -1 for append.
*
* @return the position at the end of the equality expression.
*
* @throws javax.xml.transform.TransformerException
*/
{
if (-1 == addPos)
RelationalExpr(-1);
{
{
nextToken();
nextToken();
addPos += 2;
}
else if (tokenIs('='))
{
nextToken();
addPos += 2;
}
}
return addPos;
}
/**
* .
* @returns an Object which is either a String, a Number, a Boolean, or a vector
* of nodes.
*
* RelationalExpr ::= AdditiveExpr
* | RelationalExpr '<' AdditiveExpr
* | RelationalExpr '>' AdditiveExpr
* | RelationalExpr '<=' AdditiveExpr
* | RelationalExpr '>=' AdditiveExpr
*
*
* @param addPos Position where expression is to be added, or -1 for append.
*
* @return the position at the end of the relational expression.
*
* @throws javax.xml.transform.TransformerException
*/
{
if (-1 == addPos)
AdditiveExpr(-1);
{
if (tokenIs('<'))
{
nextToken();
if (tokenIs('='))
{
nextToken();
}
else
{
}
addPos += 2;
}
else if (tokenIs('>'))
{
nextToken();
if (tokenIs('='))
{
nextToken();
}
else
{
}
addPos += 2;
}
}
return addPos;
}
/**
* This has to handle construction of the operations so that they are evaluated
* in pre-fix order. So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
* evaluated as |-|+|9|7|6|.
*
* AdditiveExpr ::= MultiplicativeExpr
* | AdditiveExpr '+' MultiplicativeExpr
* | AdditiveExpr '-' MultiplicativeExpr
*
*
* @param addPos Position where expression is to be added, or -1 for append.
*
* @return the position at the end of the equality expression.
*
* @throws javax.xml.transform.TransformerException
*/
{
if (-1 == addPos)
MultiplicativeExpr(-1);
{
if (tokenIs('+'))
{
nextToken();
addPos += 2;
}
else if (tokenIs('-'))
{
nextToken();
addPos += 2;
}
}
return addPos;
}
/**
* This has to handle construction of the operations so that they are evaluated
* in pre-fix order. So, for 9+7-6, instead of |+|9|-|7|6|, this needs to be
* evaluated as |-|+|9|7|6|.
*
* MultiplicativeExpr ::= UnaryExpr
* | MultiplicativeExpr MultiplyOperator UnaryExpr
* | MultiplicativeExpr 'div' UnaryExpr
* | MultiplicativeExpr 'mod' UnaryExpr
* | MultiplicativeExpr 'quo' UnaryExpr
*
* @param addPos Position where expression is to be added, or -1 for append.
*
* @return the position at the end of the equality expression.
*
* @throws javax.xml.transform.TransformerException
*/
{
if (-1 == addPos)
UnaryExpr();
{
if (tokenIs('*'))
{
nextToken();
addPos += 2;
}
else if (tokenIs("div"))
{
nextToken();
addPos += 2;
}
else if (tokenIs("mod"))
{
nextToken();
addPos += 2;
}
else if (tokenIs("quo"))
{
nextToken();
addPos += 2;
}
}
return addPos;
}
/**
*
* UnaryExpr ::= UnionExpr
* | '-' UnaryExpr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
boolean isNeg = false;
if (m_tokenChar == '-')
{
nextToken();
isNeg = true;
}
UnionExpr();
if (isNeg)
}
/**
*
* StringExpr ::= Expr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
Expr();
}
/**
*
*
* StringExpr ::= Expr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
Expr();
if (opLen == 2)
{
error(XPATHErrorResources.ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL, null); //"boolean(...) argument is no longer optional with 19990709 XPath draft.");
}
}
/**
*
*
* NumberExpr ::= Expr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
Expr();
}
/**
* The context of the right hand side expressions is the context of the
* left hand side expression. The results of the right hand side expressions
* are node sets. The result of the left hand side UnionExpr is the union
* of the results of the right hand side expressions.
*
*
* UnionExpr ::= PathExpr
* | UnionExpr '|' PathExpr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
boolean continueOrLoop = true;
boolean foundUnion = false;
do
{
PathExpr();
if (tokenIs('|'))
{
if (false == foundUnion)
{
foundUnion = true;
}
nextToken();
}
else
{
break;
}
// this.m_testForDocOrder = true;
}
while (continueOrLoop);
}
/**
* PathExpr ::= LocationPath
* | FilterExpr
* | FilterExpr '/' RelativeLocationPath
* | FilterExpr '//' RelativeLocationPath
*
* @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
* the error condition is severe enough to halt processing.
*
* @throws javax.xml.transform.TransformerException
*/
{
int filterExprMatch = FilterExpr();
if (filterExprMatch != FILTER_MATCH_FAILED)
{
// If FilterExpr had Predicates, a OP_LOCATIONPATH opcode would already
// have been inserted.
if (tokenIs('/'))
{
nextToken();
if (!locationPathStarted)
{
// int locationPathOpPos = opPos;
locationPathStarted = true;
}
if (!RelativeLocationPath())
{
// "Relative location path expected following '/' or '//'"
}
}
// Terminate for safety.
if (locationPathStarted)
{
}
}
else
{
LocationPath();
}
}
/**
*
*
* FilterExpr ::= PrimaryExpr
* | FilterExpr Predicate
*
* @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
* the error condition is severe enough to halt processing.
*
* @return FILTER_MATCH_PREDICATES, if this method successfully matched a
* FilterExpr with one or more Predicates;
* FILTER_MATCH_PRIMARY, if this method successfully matched a
* FilterExpr that was just a PrimaryExpr; or
* FILTER_MATCH_FAILED, if this method did not match a FilterExpr
*
* @throws javax.xml.transform.TransformerException
*/
{
int filterMatch;
if (PrimaryExpr())
{
if (tokenIs('['))
{
// int locationPathOpPos = opPos;
while (tokenIs('['))
{
Predicate();
}
}
else
{
}
}
else
{
}
return filterMatch;
/*
* if(tokenIs('['))
* {
* Predicate();
* m_ops.m_opMap[opPos + OpMap.MAPINDEX_LENGTH] = m_ops.m_opMap[OpMap.MAPINDEX_LENGTH] - opPos;
* }
*/
}
/**
*
* PrimaryExpr ::= VariableReference
* | '(' Expr ')'
* | Literal
* | Number
* | FunctionCall
*
* @return true if this method successfully matched a PrimaryExpr
*
* @throws javax.xml.transform.TransformerException
*
*/
{
boolean matchFound;
{
Literal();
matchFound = true;
}
else if (m_tokenChar == '$')
{
nextToken(); // consume '$'
QName();
matchFound = true;
}
else if (m_tokenChar == '(')
{
nextToken();
Expr();
consumeExpected(')');
matchFound = true;
}
else if ((null != m_token) && ((('.' == m_tokenChar) && (m_token.length() > 1) && Character.isDigit(
{
Number();
matchFound = true;
}
{
matchFound = FunctionCall();
}
else
{
matchFound = false;
}
return matchFound;
}
/**
*
* Argument ::= Expr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
Expr();
}
/**
*
* FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
*
* @return true if, and only if, a FunctionCall was matched
*
* @throws javax.xml.transform.TransformerException
*/
{
{
nextToken();
consumeExpected(':');
nextToken();
}
else
{
if (-1 == funcTok)
{
}
switch (funcTok)
{
case OpCodes.NODETYPE_PI :
case OpCodes.NODETYPE_COMMENT :
case OpCodes.NODETYPE_TEXT :
case OpCodes.NODETYPE_NODE :
// Node type tests look like function calls, but they're not
return false;
default :
}
nextToken();
}
consumeExpected('(');
{
if (tokenIs(','))
{
error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null); //"Found ',' but no preceding argument!");
}
Argument();
if (!tokenIs(')'))
{
consumeExpected(',');
if (tokenIs(')'))
{
null); //"Found ',' but no following argument!");
}
}
}
consumeExpected(')');
// Terminate for safety.
return true;
}
// ============= GRAMMAR FUNCTIONS =================
/**
*
* LocationPath ::= RelativeLocationPath
* | AbsoluteLocationPath
*
*
* @throws javax.xml.transform.TransformerException
*/
{
// int locationPathOpPos = opPos;
if (seenSlash)
{
// Tell how long the step is without the predicate
nextToken();
}
{
if (!RelativeLocationPath() && !seenSlash)
{
// Neither a '/' nor a RelativeLocationPath - i.e., matched nothing
// "Location path expected, but found "+m_token+" was encountered."
}
}
// Terminate for safety.
}
/**
*
* RelativeLocationPath ::= Step
* | RelativeLocationPath '/' Step
* | AbbreviatedRelativeLocationPath
*
* @returns true if, and only if, a RelativeLocationPath was matched
*
* @throws javax.xml.transform.TransformerException
*/
protected boolean RelativeLocationPath()
{
if (!Step())
{
return false;
}
while (tokenIs('/'))
{
nextToken();
if (!Step())
{
// RelativeLocationPath can't end with a trailing '/'
// "Location step expected following '/' or '//'"
}
}
return true;
}
/**
*
* Step ::= Basis Predicate
* | AbbreviatedStep
*
* @returns false if step was empty (or only a '/'); true, otherwise
*
* @throws javax.xml.transform.TransformerException
*/
{
// At most a single '/' before each Step is consumed by caller; if the
// first thing is a '/', that means we had '//' and the Step must not
// be empty.
if (doubleSlash)
{
nextToken();
// Have to fix up for patterns such as '//@foo' or '//attribute::foo',
// which translate to 'descendant-or-self::node()/attribute::foo'.
// notice I leave the '/' on the queue, so the next will be processed
// by a regular step pattern.
// Make room for telling how long the step is without the predicate
// Tell how long the step is without the predicate
// Tell how long the step is with the predicate
}
if (tokenIs("."))
{
nextToken();
if (tokenIs('['))
{
error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX, null); //"'..[predicate]' or '.[predicate]' is illegal syntax. Use 'self::node()[predicate]' instead.");
}
// Tell how long the step is without the predicate
}
else if (tokenIs(".."))
{
nextToken();
// Tell how long the step is without the predicate
}
// There is probably a better way to test for this
// transition... but it gets real hairy if you try
// to do it in basis().
{
Basis();
while (tokenIs('['))
{
Predicate();
}
// Tell how long the entire step is.
}
else
{
// No Step matched - that's an error if previous thing was a '//'
if (doubleSlash)
{
// "Location step expected following '/' or '//'"
}
return false;
}
return true;
}
/**
*
* Basis ::= AxisName '::' NodeTest
* | AbbreviatedBasis
*
* @throws javax.xml.transform.TransformerException
*/
{
int axesType;
// The next blocks guarantee that a FROM_XXX will be added.
{
nextToken();
nextToken();
}
else if (tokenIs('@'))
{
nextToken();
}
else
{
}
// Make room for telling how long the step is without the predicate
// Tell how long the step is without the predicate
}
/**
*
* Basis ::= AxisName '::' NodeTest
* | AbbreviatedBasis
*
* @return FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}.
*
* @throws javax.xml.transform.TransformerException
*/
{
{
}
return axesType;
}
/**
*
* NodeTest ::= WildcardName
* | NodeType '(' ')'
* | 'processing-instruction' '(' Literal ')'
*
* @param axesType FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}.
*
* @throws javax.xml.transform.TransformerException
*/
{
{
if (null == nodeTestOp)
{
}
else
{
nextToken();
consumeExpected('(');
{
if (!tokenIs(')'))
{
Literal();
}
}
consumeExpected(')');
}
}
else
{
// Assume name of attribute or element.
{
if (tokenIs('*'))
{
}
else
{
// Minimalist check for an NCName - just check first character
// to distinguish from other possible tokens
{
// "Node test that matches either NCName:* or QName was expected."
}
}
nextToken();
consumeExpected(':');
}
else
{
}
if (tokenIs('*'))
{
}
else
{
// Minimalist check for an NCName - just check first character
// to distinguish from other possible tokens
{
// "Node test that matches either NCName:* or QName was expected."
}
}
nextToken();
}
}
/**
*
* Predicate ::= '[' PredicateExpr ']'
*
*
* @throws javax.xml.transform.TransformerException
*/
{
if (tokenIs('['))
{
nextToken();
consumeExpected(']');
}
}
/**
*
* PredicateExpr ::= Expr
*
*
* @throws javax.xml.transform.TransformerException
*/
{
Expr();
// Terminate for safety.
}
/**
* QName ::= (Prefix ':')? LocalPart
* Prefix ::= NCName
* LocalPart ::= NCName
*
* @throws javax.xml.transform.TransformerException
*/
{
// Namespace
{
nextToken();
consumeExpected(':');
}
else
{
}
// Local name
nextToken();
}
/**
* NCName ::= (Letter | '_') (NCNameChar)
* NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
*/
protected void NCName()
{
nextToken();
}
/**
* The value of the Literal is the sequence of characters inside
* the " or ' characters>.
*
* Literal ::= '"' [^"]* '"'
* | "'" [^']* "'"
*
*
* @throws javax.xml.transform.TransformerException
*/
{
char c0 = m_tokenChar;
{
// Mutate the token to remove the quotes and have the XString object
// already made.
// lit = m_token.substring(1, last);
nextToken();
}
else
{
}
}
/**
*
* Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
*
*
* @throws javax.xml.transform.TransformerException
*/
{
{
// Mutate the token to remove the quotes and have the XNumber object
// already made.
double num;
try
{
// XPath 1.0 does not support number in exp notation
throw new NumberFormatException();
}
catch (NumberFormatException nfe)
{
}
nextToken();
}
}
// ============= PATTERN FUNCTIONS =================
/**
*
* Pattern ::= LocationPathPattern
* | Pattern '|' LocationPathPattern
*
*
* @throws javax.xml.transform.TransformerException
*/
{
while (true)
{
if (tokenIs('|'))
{
nextToken();
}
else
{
break;
}
}
}
/**
*
*
* LocationPathPattern ::= '/' RelativePathPattern?
* | IdKeyPattern (('/' | '//') RelativePathPattern)?
* | '//'? RelativePathPattern
*
*
* @throws javax.xml.transform.TransformerException
*/
{
final int RELATIVE_PATH_NOT_PERMITTED = 0;
final int RELATIVE_PATH_PERMITTED = 1;
final int RELATIVE_PATH_REQUIRED = 2;
{
IdKeyPattern();
if (tokenIs('/'))
{
nextToken();
if (tokenIs('/'))
{
nextToken();
}
else
{
}
// Tell how long the step is without the predicate
}
}
else if (tokenIs('/'))
{
{
// Added this to fix bug reported by Myriam for match="//x/a"
// patterns. If you don't do this, the 'x' step will think it's part
// of a '//' pattern, and so will cause 'a' to be matched when it has
// any ancestor that is 'x'.
nextToken();
}
else
{
}
// Tell how long the step is without the predicate
nextToken();
}
else
{
}
{
{
}
else if (relativePathStatus == RELATIVE_PATH_REQUIRED)
{
// "A relative path pattern was expected."
}
}
// Terminate for safety.
}
/**
*
* IdKeyPattern ::= 'id' '(' Literal ')'
* | 'key' '(' Literal ',' Literal ')'
* (Also handle doc())
*
*
* @throws javax.xml.transform.TransformerException
*/
{
FunctionCall();
}
/**
*
* RelativePathPattern ::= StepPattern
* | RelativePathPattern '/' StepPattern
* | RelativePathPattern '//' StepPattern
*
* @throws javax.xml.transform.TransformerException
*/
protected void RelativePathPattern()
{
// Caller will have consumed any '/' or '//' preceding the
// RelativePathPattern, so let StepPattern know it can't begin with a '/'
boolean trailingSlashConsumed = StepPattern(false);
while (tokenIs('/'))
{
nextToken();
// StepPattern() may consume first slash of pair in "a//b" while
// processing StepPattern "a". On next iteration, let StepPattern know
// that happened, so it doesn't match ill-formed patterns like "a///b".
}
}
/**
*
* StepPattern ::= AbbreviatedNodeTestStep
*
* @param isLeadingSlashPermitted a boolean indicating whether a slash can
* appear at the start of this step
*
* @return boolean indicating whether a slash following the step was consumed
*
* @throws javax.xml.transform.TransformerException
*/
{
}
/**
*
* AbbreviatedNodeTestStep ::= '@'? NodeTest Predicate
*
* @param isLeadingSlashPermitted a boolean indicating whether a slash can
* appear at the start of this step
*
* @return boolean indicating whether a slash following the step was consumed
*
* @throws javax.xml.transform.TransformerException
*/
{
int axesType;
// The next blocks guarantee that a MATCH_XXX will be added.
int matchTypePos = -1;
if (tokenIs('@'))
{
nextToken();
}
{
if (tokenIs("attribute"))
{
}
else if (tokenIs("child"))
{
}
else
{
axesType = -1;
}
nextToken();
nextToken();
}
else if (tokenIs('/'))
{
if (!isLeadingSlashPermitted)
{
// "A step was expected in the pattern, but '/' was encountered."
}
nextToken();
}
else
{
}
// Make room for telling how long the step is without the predicate
// Tell how long the step is without the predicate
while (tokenIs('['))
{
Predicate();
}
boolean trailingSlashConsumed;
// For "a//b", where "a" is current step, we need to mark operation of
// current step as "MATCH_ANY_ANCESTOR". Then we'll consume the first
// slash and subsequent step will be treated as a MATCH_IMMEDIATE_ANCESTOR
// (unless it too is followed by '//'.)
//
// %REVIEW% Following is what happens today, but I'm not sure that's
// %REVIEW% correct behaviour. Perhaps no valid case could be constructed
// %REVIEW% where it would matter?
//
// If current step is on the attribute axis (e.g., "@x//b"), we won't
// change the current step, and let following step be marked as
// MATCH_ANY_ANCESTOR on next call instead.
{
nextToken();
trailingSlashConsumed = true;
}
else
{
trailingSlashConsumed = false;
}
// Tell how long the entire step is.
return trailingSlashConsumed;
}
}