- /*
- * 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /*
- * $Id: XPathParser.java,v 1.28 2004/02/17 04:32:49 minchau Exp $
- */
- package com.sun.org.apache.xpath.internal.compiler;
-
- import javax.xml.transform.ErrorListener;
- import javax.xml.transform.TransformerException;
-
- import com.sun.org.apache.xalan.internal.res.XSLMessages;
- import com.sun.org.apache.xml.internal.utils.PrefixResolver;
- import com.sun.org.apache.xpath.internal.XPathProcessorException;
- import com.sun.org.apache.xpath.internal.objects.XNumber;
- import com.sun.org.apache.xpath.internal.objects.XString;
- import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
-
- /**
- * 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.
- static public final String CONTINUE_AFTER_FATAL_ERROR="CONTINUE_AFTER_FATAL_ERROR";
-
- /**
- * The XPath to be processed.
- */
- private OpMap m_ops;
-
- /**
- * The next token in the pattern.
- */
- transient String m_token;
-
- /**
- * 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.
- */
- transient char m_tokenChar = 0;
-
- /**
- * The position in the token queue is tracked by m_queueMark.
- */
- int m_queueMark = 0;
-
- /**
- * Results from checking FilterExpr syntax
- */
- protected final static int FILTER_MATCH_FAILED = 0;
- protected final static int FILTER_MATCH_PRIMARY = 1;
- protected final static int FILTER_MATCH_PREDICATES = 2;
-
- /**
- * The parser constructor.
- */
- public XPathParser(ErrorListener errorListener, javax.xml.transform.SourceLocator sourceLocator)
- {
- m_errorListener = errorListener;
- m_sourceLocator = sourceLocator;
- }
-
- /**
- * The prefix resolver to map prefixes to namespaces in the OpMap.
- */
- PrefixResolver m_namespaceContext;
-
- /**
- * 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(
- Compiler compiler, String expression, PrefixResolver namespaceContext)
- throws javax.xml.transform.TransformerException
- {
-
- m_ops = compiler;
- m_namespaceContext = namespaceContext;
-
- Lexer lexer = new Lexer(compiler, namespaceContext, this);
-
- lexer.tokenize(expression);
-
- m_ops.setOp(0,OpCodes.OP_XPATH);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,2);
-
-
- // 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();
-
- if (null != m_token)
- {
- String extraTokens = "";
-
- while (null != m_token)
- {
- extraTokens += "'" + m_token + "'";
-
- nextToken();
-
- if (null != m_token)
- extraTokens += ", ";
- }
-
- error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
- new Object[]{ extraTokens }); //"Extra illegal tokens: "+extraTokens);
- }
-
- }
- catch (com.sun.org.apache.xpath.internal.XPathProcessorException e)
- {
- if(CONTINUE_AFTER_FATAL_ERROR.equals(e.getMessage()))
- {
- // 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%!!!
- initXPath(compiler, "/..", namespaceContext);
- }
- else
- throw e;
- }
-
- compiler.shrink();
- }
-
- /**
- * 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(
- Compiler compiler, String expression, PrefixResolver namespaceContext)
- throws javax.xml.transform.TransformerException
- {
-
- m_ops = compiler;
- m_namespaceContext = namespaceContext;
-
- Lexer lexer = new Lexer(compiler, namespaceContext, this);
-
- lexer.tokenize(expression);
-
- m_ops.setOp(0, OpCodes.OP_MATCHPATTERN);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, 2);
-
- nextToken();
- Pattern();
-
- if (null != m_token)
- {
- String extraTokens = "";
-
- while (null != m_token)
- {
- extraTokens += "'" + m_token + "'";
-
- nextToken();
-
- if (null != m_token)
- extraTokens += ", ";
- }
-
- error(XPATHErrorResources.ER_EXTRA_ILLEGAL_TOKENS,
- new Object[]{ extraTokens }); //"Extra illegal tokens: "+extraTokens);
- }
-
- // Terminate for safety.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH)+1);
-
- m_ops.shrink();
- }
-
- /** The error listener where syntax errors are to be sent.
- */
- private ErrorListener m_errorListener;
-
- /** The source location of the XPath. */
- javax.xml.transform.SourceLocator m_sourceLocator;
-
- /**
- * 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.
- */
- public void setErrorHandler(ErrorListener handler)
- {
- m_errorListener = handler;
- }
-
- /**
- * Return the current error listener.
- *
- * @return The error listener, which should not normally be null, but may be.
- */
- public ErrorListener getErrorListener()
- {
- 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.
- */
- final boolean tokenIs(String s)
- {
- return (m_token != null) ? (m_token.equals(s)) : (s == null);
- }
-
- /**
- * 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)
- {
- return (m_token != null) ? (m_tokenChar == c) : false;
- }
-
- /**
- * 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;
-
- if ((pos <= m_ops.getTokenQueueSize()) && (pos > 0)
- && (m_ops.getTokenQueueSize() != 0))
- {
- String tok = ((String) m_ops.m_tokenQueue.elementAt(pos - 1));
-
- b = (tok.length() == 1) ? (tok.charAt(0) == c) : false;
- }
- 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;
- int lookBehindPos = m_queueMark - (n + 1);
-
- if (lookBehindPos >= 0)
- {
- String lookbehind = (String) m_ops.m_tokenQueue.elementAt(lookBehindPos);
-
- if (lookbehind.length() == 1)
- {
- char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
-
- isToken = (c0 == '|') ? false : (c0 == c);
- }
- 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)
- {
- String lookbehind = (String) m_ops.m_tokenQueue.elementAt(m_queueMark - (n - 1));
- char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
-
- hasToken = (c0 == '|') ? false : true;
- }
- 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.
- */
- private final boolean lookahead(String s, int n)
- {
-
- boolean isToken;
-
- if ((m_queueMark + n) <= m_ops.getTokenQueueSize())
- {
- String lookahead = (String) m_ops.m_tokenQueue.elementAt(m_queueMark + (n - 1));
-
- isToken = (lookahead != null) ? lookahead.equals(s) : (s == null);
- }
- else
- {
- isToken = (null == s);
- }
-
- return isToken;
- }
-
- /**
- * Retrieve the next token from the command and
- * store it in m_token string.
- */
- private final void nextToken()
- {
-
- if (m_queueMark < m_ops.getTokenQueueSize())
- {
- m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark++);
- m_tokenChar = m_token.charAt(0);
- }
- else
- {
- m_token = null;
- 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.
- */
- private final String getTokenRelative(int i)
- {
-
- String tok;
- int relative = m_queueMark + i;
-
- if ((relative > 0) && (relative < m_ops.getTokenQueueSize()))
- {
- tok = (String) m_ops.m_tokenQueue.elementAt(relative);
- }
- else
- {
- tok = null;
- }
-
- 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--;
-
- m_token = (String) m_ops.m_tokenQueue.elementAt(m_queueMark);
- m_tokenChar = m_token.charAt(0);
- }
- else
- {
- m_token = null;
- 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
- */
- private final void consumeExpected(String expected)
- throws javax.xml.transform.TransformerException
- {
-
- if (tokenIs(expected))
- {
- nextToken();
- }
- else
- {
- error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND, new Object[]{ expected,
- 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
- */
- private final void consumeExpected(char expected)
- throws javax.xml.transform.TransformerException
- {
-
- if (tokenIs(expected))
- {
- nextToken();
- }
- else
- {
- error(XPATHErrorResources.ER_EXPECTED_BUT_FOUND,
- new Object[]{ String.valueOf(expected),
- 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.
- */
- void warn(String msg, Object[] args) throws TransformerException
- {
-
- String fmsg = XSLMessages.createXPATHWarning(msg, args);
- ErrorListener ehandler = this.getErrorListener();
-
- if (null != ehandler)
- {
- // TO DO: Need to get stylesheet Locator from here.
- ehandler.warning(new TransformerException(fmsg, m_sourceLocator));
- }
- else
- {
- // Should never happen.
- System.err.println(fmsg);
- }
- }
-
- /**
- * 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.
- */
- private void assertion(boolean b, String msg)
- {
-
- if (!b)
- {
- String fMsg = XSLMessages.createXPATHMessage(
- XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
- new Object[]{ msg });
-
- 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.
- */
- void error(String msg, Object[] args) throws TransformerException
- {
-
- String fmsg = XSLMessages.createXPATHMessage(msg, args);
- ErrorListener ehandler = this.getErrorListener();
-
- TransformerException te = new TransformerException(fmsg, m_sourceLocator);
- if (null != ehandler)
- {
- // TO DO: Need to get stylesheet Locator from here.
- ehandler.fatalError(te);
- }
- 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.
- */
- protected String dumpRemainingTokenQueue()
- {
-
- int q = m_queueMark;
- String returnMsg;
-
- if (q < m_ops.getTokenQueueSize())
- {
- String msg = "\n Remaining tokens: (";
-
- while (q < m_ops.getTokenQueueSize())
- {
- String t = (String) m_ops.m_tokenQueue.elementAt(q++);
-
- msg += (" '" + t + "'");
- }
-
- returnMsg = msg + ")";
- }
- 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.
- */
- final int getFunctionToken(String key)
- {
-
- int tok;
-
- try
- {
- tok = ((Integer) (Keywords.m_functions.get(key))).intValue();
- }
- 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.
- */
- void insertOp(int pos, int length, int op)
- {
-
- int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- for (int i = totalLen - 1; i >= pos; i--)
- {
- m_ops.setOp(i + length, m_ops.getOp(i));
- }
-
- m_ops.setOp(pos,op);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,totalLen + length);
- }
-
- /**
- * 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.
- */
- void appendOp(int length, int op)
- {
-
- int totalLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- m_ops.setOp(totalLen, op);
- m_ops.setOp(totalLen + OpMap.MAPINDEX_LENGTH, length);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, totalLen + length);
- }
-
- // ============= EXPRESSIONS FUNCTIONS =================
-
- /**
- *
- *
- * Expr ::= OrExpr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Expr() throws javax.xml.transform.TransformerException
- {
- OrExpr();
- }
-
- /**
- *
- *
- * OrExpr ::= AndExpr
- * | OrExpr 'or' AndExpr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void OrExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- AndExpr();
-
- if ((null != m_token) && tokenIs("or"))
- {
- nextToken();
- insertOp(opPos, 2, OpCodes.OP_OR);
- OrExpr();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
- }
-
- /**
- *
- *
- * AndExpr ::= EqualityExpr
- * | AndExpr 'and' EqualityExpr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void AndExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- EqualityExpr(-1);
-
- if ((null != m_token) && tokenIs("and"))
- {
- nextToken();
- insertOp(opPos, 2, OpCodes.OP_AND);
- AndExpr();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
- }
-
- /**
- *
- * @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
- */
- protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if (-1 == addPos)
- addPos = opPos;
-
- RelationalExpr(-1);
-
- if (null != m_token)
- {
- if (tokenIs('!') && lookahead('=', 1))
- {
- nextToken();
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_NOTEQUALS);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = EqualityExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs('='))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_EQUALS);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = EqualityExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- 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
- */
- protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if (-1 == addPos)
- addPos = opPos;
-
- AdditiveExpr(-1);
-
- if (null != m_token)
- {
- if (tokenIs('<'))
- {
- nextToken();
-
- if (tokenIs('='))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_LTE);
- }
- else
- {
- insertOp(addPos, 2, OpCodes.OP_LT);
- }
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = RelationalExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs('>'))
- {
- nextToken();
-
- if (tokenIs('='))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_GTE);
- }
- else
- {
- insertOp(addPos, 2, OpCodes.OP_GT);
- }
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = RelationalExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- 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
- */
- protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if (-1 == addPos)
- addPos = opPos;
-
- MultiplicativeExpr(-1);
-
- if (null != m_token)
- {
- if (tokenIs('+'))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_PLUS);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = AdditiveExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs('-'))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_MINUS);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = AdditiveExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- 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
- */
- protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if (-1 == addPos)
- addPos = opPos;
-
- UnaryExpr();
-
- if (null != m_token)
- {
- if (tokenIs('*'))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_MULT);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = MultiplicativeExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs("div"))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_DIV);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = MultiplicativeExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs("mod"))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_MOD);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = MultiplicativeExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- else if (tokenIs("quo"))
- {
- nextToken();
- insertOp(addPos, 2, OpCodes.OP_QUO);
-
- int opPlusLeftHandLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - addPos;
-
- addPos = MultiplicativeExpr(addPos);
- m_ops.setOp(addPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
- addPos += 2;
- }
- }
-
- return addPos;
- }
-
- /**
- *
- * UnaryExpr ::= UnionExpr
- * | '-' UnaryExpr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void UnaryExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- boolean isNeg = false;
-
- if (m_tokenChar == '-')
- {
- nextToken();
- appendOp(2, OpCodes.OP_NEG);
-
- isNeg = true;
- }
-
- UnionExpr();
-
- if (isNeg)
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- * StringExpr ::= Expr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void StringExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- appendOp(2, OpCodes.OP_STRING);
- Expr();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- *
- * StringExpr ::= Expr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void BooleanExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- appendOp(2, OpCodes.OP_BOOL);
- Expr();
-
- int opLen = m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos;
-
- if (opLen == 2)
- {
- error(XPATHErrorResources.ER_BOOLEAN_ARG_NO_LONGER_OPTIONAL, null); //"boolean(...) argument is no longer optional with 19990709 XPath draft.");
- }
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH, opLen);
- }
-
- /**
- *
- *
- * NumberExpr ::= Expr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void NumberExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- appendOp(2, OpCodes.OP_NUMBER);
- Expr();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- * 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
- */
- protected void UnionExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- boolean continueOrLoop = true;
- boolean foundUnion = false;
-
- do
- {
- PathExpr();
-
- if (tokenIs('|'))
- {
- if (false == foundUnion)
- {
- foundUnion = true;
-
- insertOp(opPos, 2, OpCodes.OP_UNION);
- }
-
- nextToken();
- }
- else
- {
- break;
- }
-
- // this.m_testForDocOrder = true;
- }
- while (continueOrLoop);
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- * 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
- */
- protected void PathExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- int filterExprMatch = FilterExpr();
-
- if (filterExprMatch != FILTER_MATCH_FAILED)
- {
- // If FilterExpr had Predicates, a OP_LOCATIONPATH opcode would already
- // have been inserted.
- boolean locationPathStarted = (filterExprMatch==FILTER_MATCH_PREDICATES);
-
- if (tokenIs('/'))
- {
- nextToken();
-
- if (!locationPathStarted)
- {
- // int locationPathOpPos = opPos;
- insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
-
- locationPathStarted = true;
- }
-
- if (!RelativeLocationPath())
- {
- // "Relative location path expected following '/' or '//'"
- error(XPATHErrorResources.ER_EXPECTED_REL_LOC_PATH, null);
- }
-
- }
-
- // Terminate for safety.
- if (locationPathStarted)
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
- }
- 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
- */
- protected int FilterExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- int filterMatch;
-
- if (PrimaryExpr())
- {
- if (tokenIs('['))
- {
-
- // int locationPathOpPos = opPos;
- insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
-
- while (tokenIs('['))
- {
- Predicate();
- }
-
- filterMatch = FILTER_MATCH_PREDICATES;
- }
- else
- {
- filterMatch = FILTER_MATCH_PRIMARY;
- }
- }
- else
- {
- filterMatch = FILTER_MATCH_FAILED;
- }
-
- 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
- *
- */
- protected boolean PrimaryExpr() throws javax.xml.transform.TransformerException
- {
-
- boolean matchFound;
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if ((m_tokenChar == '\'') || (m_tokenChar == '"'))
- {
- appendOp(2, OpCodes.OP_LITERAL);
- Literal();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- matchFound = true;
- }
- else if (m_tokenChar == '$')
- {
- nextToken(); // consume '$'
- appendOp(2, OpCodes.OP_VARIABLE);
- QName();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- matchFound = true;
- }
- else if (m_tokenChar == '(')
- {
- nextToken();
- appendOp(2, OpCodes.OP_GROUP);
- Expr();
- consumeExpected(')');
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- matchFound = true;
- }
- else if ((null != m_token) && ((('.' == m_tokenChar) && (m_token.length() > 1) && Character.isDigit(
- m_token.charAt(1))) || Character.isDigit(m_tokenChar)))
- {
- appendOp(2, OpCodes.OP_NUMBERLIT);
- Number();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- matchFound = true;
- }
- else if (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3)))
- {
- matchFound = FunctionCall();
- }
- else
- {
- matchFound = false;
- }
-
- return matchFound;
- }
-
- /**
- *
- * Argument ::= Expr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Argument() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- appendOp(2, OpCodes.OP_ARGUMENT);
- Expr();
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- * FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
- *
- * @return true if, and only if, a FunctionCall was matched
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected boolean FunctionCall() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- if (lookahead(':', 1))
- {
- appendOp(4, OpCodes.OP_EXTFUNCTION);
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, m_queueMark - 1);
-
- nextToken();
- consumeExpected(':');
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 2, m_queueMark - 1);
-
- nextToken();
- }
- else
- {
- int funcTok = getFunctionToken(m_token);
-
- if (-1 == funcTok)
- {
- error(XPATHErrorResources.ER_COULDNOT_FIND_FUNCTION,
- new Object[]{ m_token }); //"Could not find function: "+m_token+"()");
- }
-
- 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 :
- appendOp(3, OpCodes.OP_FUNCTION);
-
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1, funcTok);
- }
-
- nextToken();
- }
-
- consumeExpected('(');
-
- while (!tokenIs(')') && m_token != null)
- {
- if (tokenIs(','))
- {
- error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null); //"Found ',' but no preceding argument!");
- }
-
- Argument();
-
- if (!tokenIs(')'))
- {
- consumeExpected(',');
-
- if (tokenIs(')'))
- {
- error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG,
- null); //"Found ',' but no following argument!");
- }
- }
- }
-
- consumeExpected(')');
-
- // Terminate for safety.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- return true;
- }
-
- // ============= GRAMMAR FUNCTIONS =================
-
- /**
- *
- * LocationPath ::= RelativeLocationPath
- * | AbsoluteLocationPath
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void LocationPath() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- // int locationPathOpPos = opPos;
- appendOp(2, OpCodes.OP_LOCATIONPATH);
-
- boolean seenSlash = tokenIs('/');
-
- if (seenSlash)
- {
- appendOp(4, OpCodes.FROM_ROOT);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_ROOT);
-
- nextToken();
- }
-
- if (m_token != null)
- {
- if (!RelativeLocationPath() && !seenSlash)
- {
- // Neither a '/' nor a RelativeLocationPath - i.e., matched nothing
- // "Location path expected, but found "+m_token+" was encountered."
- error(XPATHErrorResources.ER_EXPECTED_LOC_PATH,
- new Object [] {m_token});
- }
- }
-
- // Terminate for safety.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- * RelativeLocationPath ::= Step
- * | RelativeLocationPath '/' Step
- * | AbbreviatedRelativeLocationPath
- *
- * @returns true if, and only if, a RelativeLocationPath was matched
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected boolean RelativeLocationPath()
- throws javax.xml.transform.TransformerException
- {
- if (!Step())
- {
- return false;
- }
-
- while (tokenIs('/'))
- {
- nextToken();
-
- if (!Step())
- {
- // RelativeLocationPath can't end with a trailing '/'
- // "Location step expected following '/' or '//'"
- error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
- }
- }
-
- return true;
- }
-
- /**
- *
- * Step ::= Basis Predicate
- * | AbbreviatedStep
- *
- * @returns false if step was empty (or only a '/'); true, otherwise
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected boolean Step() throws javax.xml.transform.TransformerException
- {
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- boolean doubleSlash = tokenIs('/');
-
- // 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();
-
- appendOp(2, OpCodes.FROM_DESCENDANTS_OR_SELF);
-
- // 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
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODETYPE_NODE);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH,m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- // Tell how long the step is with the predicate
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- }
-
- if (tokenIs("."))
- {
- nextToken();
-
- if (tokenIs('['))
- {
- error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX, null); //"'..[predicate]' or '.[predicate]' is illegal syntax. Use 'self::node()[predicate]' instead.");
- }
-
- appendOp(4, OpCodes.FROM_SELF);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE);
- }
- else if (tokenIs(".."))
- {
- nextToken();
- appendOp(4, OpCodes.FROM_PARENT);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE);
- }
-
- // There is probably a better way to test for this
- // transition... but it gets real hairy if you try
- // to do it in basis().
- else if (tokenIs('*') || tokenIs('@') || tokenIs('_')
- || (m_token!= null && Character.isLetter(m_token.charAt(0))))
- {
- Basis();
-
- while (tokenIs('['))
- {
- Predicate();
- }
-
- // Tell how long the entire step is.
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
- else
- {
- // No Step matched - that's an error if previous thing was a '//'
- if (doubleSlash)
- {
- // "Location step expected following '/' or '//'"
- error(XPATHErrorResources.ER_EXPECTED_LOC_STEP, null);
- }
-
- return false;
- }
-
- return true;
- }
-
- /**
- *
- * Basis ::= AxisName '::' NodeTest
- * | AbbreviatedBasis
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Basis() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- int axesType;
-
- // The next blocks guarantee that a FROM_XXX will be added.
- if (lookahead("::", 1))
- {
- axesType = AxisName();
-
- nextToken();
- nextToken();
- }
- else if (tokenIs('@'))
- {
- axesType = OpCodes.FROM_ATTRIBUTES;
-
- appendOp(2, axesType);
- nextToken();
- }
- else
- {
- axesType = OpCodes.FROM_CHILDREN;
-
- appendOp(2, axesType);
- }
-
- // Make room for telling how long the step is without the predicate
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- NodeTest(axesType);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- * 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
- */
- protected int AxisName() throws javax.xml.transform.TransformerException
- {
-
- Object val = Keywords.m_axisnames.get(m_token);
-
- if (null == val)
- {
- error(XPATHErrorResources.ER_ILLEGAL_AXIS_NAME,
- new Object[]{ m_token }); //"illegal axis name: "+m_token);
- }
-
- int axesType = ((Integer) val).intValue();
-
- appendOp(2, axesType);
-
- 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
- */
- protected void NodeTest(int axesType) throws javax.xml.transform.TransformerException
- {
-
- if (lookahead('(', 1))
- {
- Object nodeTestOp = Keywords.m_nodetypes.get(m_token);
-
- if (null == nodeTestOp)
- {
- error(XPATHErrorResources.ER_UNKNOWN_NODETYPE,
- new Object[]{ m_token }); //"Unknown nodetype: "+m_token);
- }
- else
- {
- nextToken();
-
- int nt = ((Integer) nodeTestOp).intValue();
-
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), nt);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- consumeExpected('(');
-
- if (OpCodes.NODETYPE_PI == nt)
- {
- if (!tokenIs(')'))
- {
- Literal();
- }
- }
-
- consumeExpected(')');
- }
- }
- else
- {
-
- // Assume name of attribute or element.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODENAME);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- if (lookahead(':', 1))
- {
- if (tokenIs('*'))
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
- }
- else
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
-
- // Minimalist check for an NCName - just check first character
- // to distinguish from other possible tokens
- if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
- {
- // "Node test that matches either NCName:* or QName was expected."
- error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
- }
- }
-
- nextToken();
- consumeExpected(':');
- }
- else
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.EMPTY);
- }
-
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- if (tokenIs('*'))
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
- }
- else
- {
- if (OpCodes.FROM_NAMESPACE == axesType)
- {
- String prefix = (String) this.m_ops.m_tokenQueue.elementAt(m_queueMark - 1);
- String namespace =
- ((PrefixResolver) m_namespaceContext).getNamespaceForPrefix(
- prefix);
-
- this.m_ops.m_tokenQueue.setElementAt(namespace,m_queueMark - 1);
- }
-
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
-
- // Minimalist check for an NCName - just check first character
- // to distinguish from other possible tokens
- if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
- {
- // "Node test that matches either NCName:* or QName was expected."
- error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
- }
- }
-
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- }
- }
-
- /**
- *
- * Predicate ::= '[' PredicateExpr ']'
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Predicate() throws javax.xml.transform.TransformerException
- {
-
- if (tokenIs('['))
- {
- nextToken();
- PredicateExpr();
- consumeExpected(']');
- }
- }
-
- /**
- *
- * PredicateExpr ::= Expr
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void PredicateExpr() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- appendOp(2, OpCodes.OP_PREDICATE);
- Expr();
-
- // Terminate for safety.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- * QName ::= (Prefix ':')? LocalPart
- * Prefix ::= NCName
- * LocalPart ::= NCName
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void QName() throws javax.xml.transform.TransformerException
- {
- // Namespace
- if(lookahead(':', 1))
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- consumeExpected(':');
- }
- else
- {
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.EMPTY);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- }
-
- // Local name
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- }
-
- /**
- * NCName ::= (Letter | '_') (NCNameChar)
- * NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
- */
- protected void NCName()
- {
-
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- }
-
- /**
- * The value of the Literal is the sequence of characters inside
- * the " or ' characters>.
- *
- * Literal ::= '"' [^"]* '"'
- * | "'" [^']* "'"
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Literal() throws javax.xml.transform.TransformerException
- {
-
- int last = m_token.length() - 1;
- char c0 = m_tokenChar;
- char cX = m_token.charAt(last);
-
- if (((c0 == '\"') && (cX == '\"')) || ((c0 == '\'') && (cX == '\'')))
- {
-
- // Mutate the token to remove the quotes and have the XString object
- // already made.
- int tokenQueuePos = m_queueMark - 1;
-
- m_ops.m_tokenQueue.setElementAt(null,tokenQueuePos);
-
- Object obj = new XString(m_token.substring(1, last));
-
- m_ops.m_tokenQueue.setElementAt(obj,tokenQueuePos);
-
- // lit = m_token.substring(1, last);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), tokenQueuePos);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- }
- else
- {
- error(XPATHErrorResources.ER_PATTERN_LITERAL_NEEDS_BE_QUOTED,
- new Object[]{ m_token }); //"Pattern literal ("+m_token+") needs to be quoted!");
- }
- }
-
- /**
- *
- * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Number() throws javax.xml.transform.TransformerException
- {
-
- if (null != m_token)
- {
-
- // 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
- if ((m_token.indexOf('e') > -1)||(m_token.indexOf('E') > -1))
- throw new NumberFormatException();
- num = Double.valueOf(m_token).doubleValue();
- }
- catch (NumberFormatException nfe)
- {
- num = 0.0; // to shut up compiler.
-
- error(XPATHErrorResources.ER_COULDNOT_BE_FORMATTED_TO_NUMBER,
- new Object[]{ m_token }); //m_token+" could not be formatted to a number!");
- }
-
- m_ops.m_tokenQueue.setElementAt(new XNumber(num),m_queueMark - 1);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- nextToken();
- }
- }
-
- // ============= PATTERN FUNCTIONS =================
-
- /**
- *
- * Pattern ::= LocationPathPattern
- * | Pattern '|' LocationPathPattern
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void Pattern() throws javax.xml.transform.TransformerException
- {
-
- while (true)
- {
- LocationPathPattern();
-
- if (tokenIs('|'))
- {
- nextToken();
- }
- else
- {
- break;
- }
- }
- }
-
- /**
- *
- *
- * LocationPathPattern ::= '/' RelativePathPattern?
- * | IdKeyPattern (('/' | '//') RelativePathPattern)?
- * | '//'? RelativePathPattern
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void LocationPathPattern() throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
-
- final int RELATIVE_PATH_NOT_PERMITTED = 0;
- final int RELATIVE_PATH_PERMITTED = 1;
- final int RELATIVE_PATH_REQUIRED = 2;
-
- int relativePathStatus = RELATIVE_PATH_NOT_PERMITTED;
-
- appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN);
-
- if (lookahead('(', 1)
- && (tokenIs(Keywords.FUNC_ID_STRING)
- || tokenIs(Keywords.FUNC_KEY_STRING)))
- {
- IdKeyPattern();
-
- if (tokenIs('/'))
- {
- nextToken();
-
- if (tokenIs('/'))
- {
- appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
-
- nextToken();
- }
- else
- {
- appendOp(4, OpCodes.MATCH_IMMEDIATE_ANCESTOR);
- }
-
- // Tell how long the step is without the predicate
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_FUNCTEST);
-
- relativePathStatus = RELATIVE_PATH_REQUIRED;
- }
- }
- else if (tokenIs('/'))
- {
- if (lookahead('/', 1))
- {
- appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
-
- // 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();
-
- relativePathStatus = RELATIVE_PATH_REQUIRED;
- }
- else
- {
- appendOp(4, OpCodes.FROM_ROOT);
-
- relativePathStatus = RELATIVE_PATH_PERMITTED;
- }
-
-
- // Tell how long the step is without the predicate
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2, 4);
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_ROOT);
-
- nextToken();
- }
- else
- {
- relativePathStatus = RELATIVE_PATH_REQUIRED;
- }
-
- if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED)
- {
- if (!tokenIs('|') && (null != m_token))
- {
- RelativePathPattern();
- }
- else if (relativePathStatus == RELATIVE_PATH_REQUIRED)
- {
- // "A relative path pattern was expected."
- error(XPATHErrorResources.ER_EXPECTED_REL_PATH_PATTERN, null);
- }
- }
-
- // Terminate for safety.
- m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
- }
-
- /**
- *
- * IdKeyPattern ::= 'id' '(' Literal ')'
- * | 'key' '(' Literal ',' Literal ')'
- * (Also handle doc())
- *
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void IdKeyPattern() throws javax.xml.transform.TransformerException
- {
- FunctionCall();
- }
-
- /**
- *
- * RelativePathPattern ::= StepPattern
- * | RelativePathPattern '/' StepPattern
- * | RelativePathPattern '//' StepPattern
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void RelativePathPattern()
- throws javax.xml.transform.TransformerException
- {
-
- // 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".
- trailingSlashConsumed = StepPattern(!trailingSlashConsumed);
- }
- }
-
- /**
- *
- * 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
- */
- protected boolean StepPattern(boolean isLeadingSlashPermitted)
- throws javax.xml.transform.TransformerException
- {
- return AbbreviatedNodeTestStep(isLeadingSlashPermitted);
- }
-
- /**
- *
- * 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
- */
- protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted)
- throws javax.xml.transform.TransformerException
- {
-
- int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- int axesType;
-
- // The next blocks guarantee that a MATCH_XXX will be added.
- int matchTypePos = -1;
-
- if (tokenIs('@'))
- {
- axesType = OpCodes.MATCH_ATTRIBUTE;
-
- appendOp(2, axesType);
- nextToken();
- }
- else if (this.lookahead("::", 1))
- {
- if (tokenIs("attribute"))
- {
- axesType = OpCodes.MATCH_ATTRIBUTE;
-
- appendOp(2, axesType);
- }
- else if (tokenIs("child"))
- {
- matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
-
- appendOp(2, axesType);
- }
- else
- {
- axesType = -1;
-
- this.error(XPATHErrorResources.ER_AXES_NOT_ALLOWED,
- new Object[]{ this.m_token });
- }
-
- nextToken();
- nextToken();
- }
- else if (tokenIs('/'))
- {
- if (!isLeadingSlashPermitted)
- {
- // "A step was expected in the pattern, but '/' was encountered."
- error(XPATHErrorResources.ER_EXPECTED_STEP_PATTERN, null);
- }
- axesType = OpCodes.MATCH_ANY_ANCESTOR;
-
- appendOp(2, axesType);
- nextToken();
- }
- else
- {
- matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
- axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
-
- appendOp(2, axesType);
- }
-
- // Make room for telling how long the step is without the predicate
- m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
-
- NodeTest(axesType);
-
- // Tell how long the step is without the predicate
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- 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.
- if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1))
- {
- m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR);
-
- nextToken();
-
- trailingSlashConsumed = true;
- }
- else
- {
- trailingSlashConsumed = false;
- }
-
- // Tell how long the entire step is.
- m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
- m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
-
- return trailingSlashConsumed;
- }
- }