/* GNU GENERAL LICENSE Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either verion 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License for more details. You should have received a copy of the GNU General Public along with this program. If not, see <http://www.gnu.org/licenses/>. Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it */ /* * $Id: XPathEvaluatorImpl.java 1225443 2011-12-29 05:44:18Z mrglavas $ */ package org.lobobrowser.html.xpath; import javax.xml.transform.TransformerException; import org.apache.xml.utils.PrefixResolver; import org.apache.xpath.XPath; import org.apache.xpath.domapi.XPathStylesheetDOM3Exception; import org.apache.xpath.res.XPATHErrorResources; import org.apache.xpath.res.XPATHMessages; import org.lobobrowser.w3c.xpath.XPathEvaluator; import org.lobobrowser.w3c.xpath.XPathException; import org.lobobrowser.w3c.xpath.XPathExpression; import org.lobobrowser.w3c.xpath.XPathNSResolver; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * * The class provides an implementation of XPathEvaluator according to the DOM * L3 XPath Specification, Working Group Note 26 February 2004. * * <p> * See also the * <a href='http://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226'>Document * Object Model (DOM) Level 3 XPath Specification</a>.</p> * * </p>The evaluation of XPath expressions is provided by * <code>XPathEvaluator</code>, which will provide evaluation of XPath 1.0 * expressions with no specialized extension functions or variables. It is * expected that the <code>XPathEvaluator</code> interface will be implemented * on the same object which implements the <code>Document</code> interface in an * implementation which supports the XPath DOM module. * <code>XPathEvaluator</code> implementations may be available from other * sources that may provide support for special extension functions or variables * which are not defined in this specification.</p> * * @see org.w3c.dom.xpath.XPathEvaluator * * @xsl.usage internal */ public final class XPathEvaluatorImpl implements XPathEvaluator { /** * This prefix resolver is created whenever null is passed to the evaluate * method. Its purpose is to satisfy the DOM L3 XPath API requirement that * if a null prefix resolver is used, an exception should only be thrown * when an attempt is made to resolve a prefix. */ private static class DummyPrefixResolver implements PrefixResolver { /** * Constructor for DummyPrefixResolver. */ DummyPrefixResolver() { } /** * @exception DOMException * NAMESPACE_ERR: Always throws this exceptionn * * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String, * Node) */ @Override public String getNamespaceForPrefix(String prefix, Node context) { String fmsg = XPATHMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_RESOLVER, null); throw new DOMException(DOMException.NAMESPACE_ERR, fmsg); } /** * @exception DOMException * NAMESPACE_ERR: Always throws this exceptionn * * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String) */ @Override public String getNamespaceForPrefix(String prefix) { return getNamespaceForPrefix(prefix, null); } /** * @see org.apache.xml.utils.PrefixResolver#handlesNullPrefixes() */ @Override public boolean handlesNullPrefixes() { return false; } /** * @see org.apache.xml.utils.PrefixResolver#getBaseIdentifier() */ @Override public String getBaseIdentifier() { return null; } } /** * The document to be searched to parallel the case where the XPathEvaluator * is obtained by casting a Document. */ private final Document m_doc; /** * Constructor for XPathEvaluatorImpl. * * @param doc * The document to be searched, to parallel the case where'' the * XPathEvaluator is obtained by casting the document. */ public XPathEvaluatorImpl(Document doc) { m_doc = doc; } /** * Constructor in the case that the XPath expression can be evaluated * without needing an XML document at all. * */ public XPathEvaluatorImpl() { m_doc = null; } /** * Creates a parsed XPath expression with resolved namespaces. This is * useful when an expression will be reused in an application since it makes * it possible to compile the expression string into a more efficient * internal form and preresolve all namespace prefixes which occur within * the expression. * * @param expression * The XPath expression string to be parsed. * @param resolver * The <code>resolver</code> permits translation of prefixes * within the XPath expression into appropriate namespace URIs . * If this is specified as <code>null</code>, any namespace * prefix within the expression will result in * <code>DOMException</code> being thrown with the code * <code>NAMESPACE_ERR</code>. * @return The compiled form of the XPath expression. * @exception XPathException * INVALID_EXPRESSION_ERR: Raised if the expression is not * legal according to the rules of the * <code>XPathEvaluator</code>i * @exception DOMException * NAMESPACE_ERR: Raised if the expression contains namespace * prefixes which cannot be resolved by the specified * <code>XPathNSResolver</code>. * * @see org.w3c.dom.xpath.XPathEvaluator#createExpression(String, * XPathNSResolver) */ @Override public XPathExpression createExpression(String expression, XPathNSResolver resolver) throws XPathException, DOMException { try { // If the resolver is null, create a dummy prefix resolver XPath xpath = new XPath(expression, null, ((null == resolver) ? new DummyPrefixResolver() : ((PrefixResolver) resolver)), XPath.SELECT); return new XPathExpressionImpl(xpath, m_doc); } catch (TransformerException e) { // Need to pass back exception code DOMException.NAMESPACE_ERR also. // Error found in DOM Level 3 XPath Test Suite. if (e instanceof XPathStylesheetDOM3Exception) { throw new DOMException(DOMException.NAMESPACE_ERR, e.getMessageAndLocation()); } else { throw new XPathException(XPathException.INVALID_EXPRESSION_ERR, e.getMessageAndLocation()); } } } /** * Adapts any DOM node to resolve namespaces so that an XPath expression can * be easily evaluated relative to the context of the node where it appeared * within the document. This adapter works like the DOM Level 3 method * <code>lookupNamespaceURI</code> on nodes in resolving the namespaceURI * from a given prefix using the current information available in the node's * hierarchy at the time lookupNamespaceURI is called, also correctly * resolving the implicit xml prefix. * * @param nodeResolver * The node to be used as a context for namespace resolution. * @return <code>XPathNSResolver</code> which resolves namespaces with * respect to the definitions in scope for a specified node. * * @see org.w3c.dom.xpath.XPathEvaluator#createNSResolver(Node) */ @Override public XPathNSResolver createNSResolver(Node nodeResolver) { return new XPathNSResolverImpl((nodeResolver.getNodeType() == Node.DOCUMENT_NODE) ? ((Document) nodeResolver).getDocumentElement() : nodeResolver); } /** * Evaluates an XPath expression string and returns a result of the * specified type if possible. * * @param expression * The XPath expression string to be parsed and evaluated. * @param contextNode * The <code>context</code> is context node for the evaluation of * this XPath expression. If the XPathEvaluator was obtained by * casting the <code>Document</code> then this must be owned by * the same document and must be a <code>Document</code>, * <code>Element</code>, <code>Attribute</code>, * <code>Text</code>, <code>CDATASection</code>, * <code>Comment</code>, <code>ProcessingInstruction</code>, or * <code>XPathNamespace</code> node. If the context node is a * <code>Text</code> or a <code>CDATASection</code>, then the * context is interpreted as the whole logical text node as seen * by XPath, unless the node is empty in which case it may not * serve as the XPath context. * @param resolver * The <code>resolver</code> permits translation of prefixes * within the XPath expression into appropriate namespace URIs . * If this is specified as <code>null</code>, any namespace * prefix within the expression will result in * <code>DOMException</code> being thrown with the code * <code>NAMESPACE_ERR</code>. * @param type * If a specific <code>type</code> is specified, then the result * will be coerced to return the specified type relying on XPath * type conversions and fail if the desired coercion is not * possible. This must be one of the type codes of * <code>XPathResult</code>. * @param result * The <code>result</code> specifies a specific result object * which may be reused and returned by this method. If this is * specified as <code>null</code>or the implementation does not * reuse the specified result, a new result object will be * constructed and returned.For XPath 1.0 results, this object * will be of type <code>XPathResult</code>. * @return The result of the evaluation of the XPath expression.For XPath * 1.0 results, this object will be of type <code>XPathResult</code> * . * @exception XPathException * INVALID_EXPRESSION_ERR: Raised if the expression is not * legal according to the rules of the * <code>XPathEvaluator</code>i <br> * TYPE_ERR: Raised if the result cannot be converted to * return the specified type. * @exception DOMException * NAMESPACE_ERR: Raised if the expression contains namespace * prefixes which cannot be resolved by the specified * <code>XPathNSResolver</code>. <br> * WRONG_DOCUMENT_ERR: The Node is from a document that is * not supported by this XPathEvaluator. <br> * NOT_SUPPORTED_ERR: The Node is not a type permitted as an * XPath context node. * * @see org.w3c.dom.xpath.XPathEvaluator#evaluate(String, Node, * XPathNSResolver, short, XPathResult) */ @Override public Object evaluate(String expression, Node contextNode, XPathNSResolver resolver, short type, Object result) throws XPathException, DOMException { XPathExpression xpathExpression = createExpression(expression, resolver); return xpathExpression.evaluate(contextNode, type, result); } }