📄 xpathparser.java
字号:
/* * 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.2.4.1 2005/09/14 19:46:02 jeffsuttor 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.domapi.XPathStylesheetDOM3Exception;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; m_functionTable = compiler.getFunctionTable(); 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; m_functionTable = compiler.getFunctionTable(); 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; /** The table contains build-in functions and customized functions */ private FunctionTable m_functionTable; /** * 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; } } /**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -