001: /*
002: * Copyright 2002-2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id$
018: */
019:
020: package org.apache.xpath.domapi;
021:
022: import javax.xml.transform.TransformerException;
023:
024: import org.apache.xml.utils.PrefixResolver;
025: import org.apache.xpath.XPath;
026: import org.apache.xpath.res.XPATHErrorResources;
027: import org.apache.xpath.res.XPATHMessages;
028: import org.w3c.dom.DOMException;
029: import org.w3c.dom.Document;
030: import org.w3c.dom.Node;
031: import org.w3c.dom.xpath.XPathEvaluator;
032: import org.w3c.dom.xpath.XPathException;
033: import org.w3c.dom.xpath.XPathExpression;
034: import org.w3c.dom.xpath.XPathNSResolver;
035:
036: /**
037: *
038: * The class provides an implementation of XPathEvaluator according
039: * to the DOM L3 XPath Specification, Working Group Note 26 February 2004.
040: *
041: * <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>
042: *
043: * </p>The evaluation of XPath expressions is provided by
044: * <code>XPathEvaluator</code>, which will provide evaluation of XPath 1.0
045: * expressions with no specialized extension functions or variables. It is
046: * expected that the <code>XPathEvaluator</code> interface will be
047: * implemented on the same object which implements the <code>Document</code>
048: * interface in an implementation which supports the XPath DOM module.
049: * <code>XPathEvaluator</code> implementations may be available from other
050: * sources that may provide support for special extension functions or
051: * variables which are not defined in this specification.</p>
052: *
053: * @see org.w3c.dom.xpath.XPathEvaluator
054: *
055: * @xsl.usage internal
056: */
057: public final class XPathEvaluatorImpl implements XPathEvaluator {
058:
059: /**
060: * This prefix resolver is created whenever null is passed to the
061: * evaluate method. Its purpose is to satisfy the DOM L3 XPath API
062: * requirement that if a null prefix resolver is used, an exception
063: * should only be thrown when an attempt is made to resolve a prefix.
064: */
065: private class DummyPrefixResolver implements PrefixResolver {
066:
067: /**
068: * Constructor for DummyPrefixResolver.
069: */
070: DummyPrefixResolver() {
071: }
072:
073: /**
074: * @exception DOMException
075: * NAMESPACE_ERR: Always throws this exceptionn
076: *
077: * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String, Node)
078: */
079: public String getNamespaceForPrefix(String prefix, Node context) {
080: String fmsg = XPATHMessages.createXPATHMessage(
081: XPATHErrorResources.ER_NULL_RESOLVER, null);
082: throw new DOMException(DOMException.NAMESPACE_ERR, fmsg); // Unable to resolve prefix with null prefix resolver.
083: }
084:
085: /**
086: * @exception DOMException
087: * NAMESPACE_ERR: Always throws this exceptionn
088: *
089: * @see org.apache.xml.utils.PrefixResolver#getNamespaceForPrefix(String)
090: */
091: public String getNamespaceForPrefix(String prefix) {
092: return getNamespaceForPrefix(prefix, null);
093: }
094:
095: /**
096: * @see org.apache.xml.utils.PrefixResolver#handlesNullPrefixes()
097: */
098: public boolean handlesNullPrefixes() {
099: return false;
100: }
101:
102: /**
103: * @see org.apache.xml.utils.PrefixResolver#getBaseIdentifier()
104: */
105: public String getBaseIdentifier() {
106: return null;
107: }
108:
109: }
110:
111: /**
112: * The document to be searched to parallel the case where the XPathEvaluator
113: * is obtained by casting a Document.
114: */
115: private final Document m_doc;
116:
117: /**
118: * Constructor for XPathEvaluatorImpl.
119: *
120: * @param doc The document to be searched, to parallel the case where''
121: * the XPathEvaluator is obtained by casting the document.
122: */
123: public XPathEvaluatorImpl(Document doc) {
124: m_doc = doc;
125: }
126:
127: /**
128: * Constructor in the case that the XPath expression can be evaluated
129: * without needing an XML document at all.
130: *
131: */
132: public XPathEvaluatorImpl() {
133: m_doc = null;
134: }
135:
136: /**
137: * Creates a parsed XPath expression with resolved namespaces. This is
138: * useful when an expression will be reused in an application since it
139: * makes it possible to compile the expression string into a more
140: * efficient internal form and preresolve all namespace prefixes which
141: * occur within the expression.
142: *
143: * @param expression The XPath expression string to be parsed.
144: * @param resolver The <code>resolver</code> permits translation of
145: * prefixes within the XPath expression into appropriate namespace URIs
146: * . If this is specified as <code>null</code>, any namespace prefix
147: * within the expression will result in <code>DOMException</code>
148: * being thrown with the code <code>NAMESPACE_ERR</code>.
149: * @return The compiled form of the XPath expression.
150: * @exception XPathException
151: * INVALID_EXPRESSION_ERR: Raised if the expression is not legal
152: * according to the rules of the <code>XPathEvaluator</code>i
153: * @exception DOMException
154: * NAMESPACE_ERR: Raised if the expression contains namespace prefixes
155: * which cannot be resolved by the specified
156: * <code>XPathNSResolver</code>.
157: *
158: * @see org.w3c.dom.xpath.XPathEvaluator#createExpression(String, XPathNSResolver)
159: */
160: public XPathExpression createExpression(String expression,
161: XPathNSResolver resolver) throws XPathException,
162: DOMException {
163:
164: try {
165:
166: // If the resolver is null, create a dummy prefix resolver
167: XPath xpath = new XPath(expression, null,
168: ((null == resolver) ? new DummyPrefixResolver()
169: : ((PrefixResolver) resolver)),
170: XPath.SELECT);
171:
172: return new XPathExpressionImpl(xpath, m_doc);
173:
174: } catch (TransformerException e) {
175: // Need to pass back exception code DOMException.NAMESPACE_ERR also.
176: // Error found in DOM Level 3 XPath Test Suite.
177: if (e instanceof XPathStylesheetDOM3Exception)
178: throw new DOMException(DOMException.NAMESPACE_ERR, e
179: .getMessageAndLocation());
180: else
181: throw new XPathException(
182: XPathException.INVALID_EXPRESSION_ERR, e
183: .getMessageAndLocation());
184:
185: }
186: }
187:
188: /**
189: * Adapts any DOM node to resolve namespaces so that an XPath expression
190: * can be easily evaluated relative to the context of the node where it
191: * appeared within the document. This adapter works like the DOM Level 3
192: * method <code>lookupNamespaceURI</code> on nodes in resolving the
193: * namespaceURI from a given prefix using the current information available
194: * in the node's hierarchy at the time lookupNamespaceURI is called, also
195: * correctly resolving the implicit xml prefix.
196: *
197: * @param nodeResolver The node to be used as a context for namespace
198: * resolution.
199: * @return <code>XPathNSResolver</code> which resolves namespaces with
200: * respect to the definitions in scope for a specified node.
201: *
202: * @see org.w3c.dom.xpath.XPathEvaluator#createNSResolver(Node)
203: */
204: public XPathNSResolver createNSResolver(Node nodeResolver) {
205:
206: return new XPathNSResolverImpl(
207: (nodeResolver.getNodeType() == Node.DOCUMENT_NODE) ? ((Document) nodeResolver)
208: .getDocumentElement()
209: : nodeResolver);
210: }
211:
212: /**
213: * Evaluates an XPath expression string and returns a result of the
214: * specified type if possible.
215: *
216: * @param expression The XPath expression string to be parsed and
217: * evaluated.
218: * @param contextNode The <code>context</code> is context node for the
219: * evaluation of this XPath expression. If the XPathEvaluator was
220: * obtained by casting the <code>Document</code> then this must be
221: * owned by the same document and must be a <code>Document</code>,
222: * <code>Element</code>, <code>Attribute</code>, <code>Text</code>,
223: * <code>CDATASection</code>, <code>Comment</code>,
224: * <code>ProcessingInstruction</code>, or <code>XPathNamespace</code>
225: * node. If the context node is a <code>Text</code> or a
226: * <code>CDATASection</code>, then the context is interpreted as the
227: * whole logical text node as seen by XPath, unless the node is empty
228: * in which case it may not serve as the XPath context.
229: * @param resolver The <code>resolver</code> permits translation of
230: * prefixes within the XPath expression into appropriate namespace URIs
231: * . If this is specified as <code>null</code>, any namespace prefix
232: * within the expression will result in <code>DOMException</code>
233: * being thrown with the code <code>NAMESPACE_ERR</code>.
234: * @param type If a specific <code>type</code> is specified, then the
235: * result will be coerced to return the specified type relying on
236: * XPath type conversions and fail if the desired coercion is not
237: * possible. This must be one of the type codes of
238: * <code>XPathResult</code>.
239: * @param result The <code>result</code> specifies a specific result
240: * object which may be reused and returned by this method. If this is
241: * specified as <code>null</code>or the implementation does not reuse
242: * the specified result, a new result object will be constructed and
243: * returned.For XPath 1.0 results, this object will be of type
244: * <code>XPathResult</code>.
245: * @return The result of the evaluation of the XPath expression.For XPath
246: * 1.0 results, this object will be of type <code>XPathResult</code>.
247: * @exception XPathException
248: * INVALID_EXPRESSION_ERR: Raised if the expression is not legal
249: * according to the rules of the <code>XPathEvaluator</code>i
250: * <br>TYPE_ERR: Raised if the result cannot be converted to return the
251: * specified type.
252: * @exception DOMException
253: * NAMESPACE_ERR: Raised if the expression contains namespace prefixes
254: * which cannot be resolved by the specified
255: * <code>XPathNSResolver</code>.
256: * <br>WRONG_DOCUMENT_ERR: The Node is from a document that is not
257: * supported by this XPathEvaluator.
258: * <br>NOT_SUPPORTED_ERR: The Node is not a type permitted as an XPath
259: * context node.
260: *
261: * @see org.w3c.dom.xpath.XPathEvaluator#evaluate(String, Node, XPathNSResolver, short, XPathResult)
262: */
263: public Object evaluate(String expression, Node contextNode,
264: XPathNSResolver resolver, short type, Object result)
265: throws XPathException, DOMException {
266:
267: XPathExpression xpathExpression = createExpression(expression,
268: resolver);
269:
270: return xpathExpression.evaluate(contextNode, type, result);
271: }
272:
273: }
|