/*
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);
}
}