1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: XPathAPI.java 524807 2007-04-02 15:51:43Z zongaro $ 20 */ 21 package org.apache.xpath; 22 23 import javax.xml.transform.TransformerException; 24 25 import org.apache.xml.utils.PrefixResolver; 26 import org.apache.xml.utils.PrefixResolverDefault; 27 import org.apache.xpath.objects.XObject; 28 29 import org.w3c.dom.Document; 30 import org.w3c.dom.Node; 31 import org.w3c.dom.NodeList; 32 import org.w3c.dom.traversal.NodeIterator; 33 34 /** 35 * The methods in this class are convenience methods into the 36 * low-level XPath API. 37 * These functions tend to be a little slow, since a number of objects must be 38 * created for each evaluation. A faster way is to precompile the 39 * XPaths using the low-level API, and then just use the XPaths 40 * over and over. 41 * 42 * NOTE: In particular, each call to this method will create a new 43 * XPathContext, a new DTMManager... and thus a new DTM. That's very 44 * safe, since it guarantees that you're always processing against a 45 * fully up-to-date view of your document. But it's also portentially 46 * very expensive, since you're rebuilding the DTM every time. You should 47 * consider using an instance of CachedXPathAPI rather than these static 48 * methods. 49 * 50 * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a> 51 * */ 52 public class XPathAPI 53 { 54 55 /** 56 * Use an XPath string to select a single node. XPath namespace 57 * prefixes are resolved from the context node, which may not 58 * be what you want (see the next method). 59 * 60 * @param contextNode The node to start searching from. 61 * @param str A valid XPath string. 62 * @return The first node found that matches the XPath, or null. 63 * 64 * @throws TransformerException 65 */ 66 public static Node selectSingleNode(Node contextNode, String str) 67 throws TransformerException 68 { 69 return selectSingleNode(contextNode, str, contextNode); 70 } 71 72 /** 73 * Use an XPath string to select a single node. 74 * XPath namespace prefixes are resolved from the namespaceNode. 75 * 76 * @param contextNode The node to start searching from. 77 * @param str A valid XPath string. 78 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 79 * @return The first node found that matches the XPath, or null. 80 * 81 * @throws TransformerException 82 */ 83 public static Node selectSingleNode( 84 Node contextNode, String str, Node namespaceNode) 85 throws TransformerException 86 { 87 88 // Have the XObject return its result as a NodeSetDTM. 89 NodeIterator nl = selectNodeIterator(contextNode, str, namespaceNode); 90 91 // Return the first node, or null 92 return nl.nextNode(); 93 } 94 95 /** 96 * Use an XPath string to select a nodelist. 97 * XPath namespace prefixes are resolved from the contextNode. 98 * 99 * @param contextNode The node to start searching from. 100 * @param str A valid XPath string. 101 * @return A NodeIterator, should never be null. 102 * 103 * @throws TransformerException 104 */ 105 public static NodeIterator selectNodeIterator(Node contextNode, String str) 106 throws TransformerException 107 { 108 return selectNodeIterator(contextNode, str, contextNode); 109 } 110 111 /** 112 * Use an XPath string to select a nodelist. 113 * XPath namespace prefixes are resolved from the namespaceNode. 114 * 115 * @param contextNode The node to start searching from. 116 * @param str A valid XPath string. 117 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 118 * @return A NodeIterator, should never be null. 119 * 120 * @throws TransformerException 121 */ 122 public static NodeIterator selectNodeIterator( 123 Node contextNode, String str, Node namespaceNode) 124 throws TransformerException 125 { 126 127 // Execute the XPath, and have it return the result 128 XObject list = eval(contextNode, str, namespaceNode); 129 130 // Have the XObject return its result as a NodeSetDTM. 131 return list.nodeset(); 132 } 133 134 /** 135 * Use an XPath string to select a nodelist. 136 * XPath namespace prefixes are resolved from the contextNode. 137 * 138 * @param contextNode The node to start searching from. 139 * @param str A valid XPath string. 140 * @return A NodeIterator, should never be null. 141 * 142 * @throws TransformerException 143 */ 144 public static NodeList selectNodeList(Node contextNode, String str) 145 throws TransformerException 146 { 147 return selectNodeList(contextNode, str, contextNode); 148 } 149 150 /** 151 * Use an XPath string to select a nodelist. 152 * XPath namespace prefixes are resolved from the namespaceNode. 153 * 154 * @param contextNode The node to start searching from. 155 * @param str A valid XPath string. 156 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 157 * @return A NodeIterator, should never be null. 158 * 159 * @throws TransformerException 160 */ 161 public static NodeList selectNodeList( 162 Node contextNode, String str, Node namespaceNode) 163 throws TransformerException 164 { 165 166 // Execute the XPath, and have it return the result 167 XObject list = eval(contextNode, str, namespaceNode); 168 169 // Return a NodeList. 170 return list.nodelist(); 171 } 172 173 /** 174 * Evaluate XPath string to an XObject. Using this method, 175 * XPath namespace prefixes will be resolved from the namespaceNode. 176 * @param contextNode The node to start searching from. 177 * @param str A valid XPath string. 178 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 179 * @see org.apache.xpath.objects.XObject 180 * @see org.apache.xpath.objects.XNull 181 * @see org.apache.xpath.objects.XBoolean 182 * @see org.apache.xpath.objects.XNumber 183 * @see org.apache.xpath.objects.XString 184 * @see org.apache.xpath.objects.XRTreeFrag 185 * 186 * @throws TransformerException 187 */ 188 public static XObject eval(Node contextNode, String str) 189 throws TransformerException 190 { 191 return eval(contextNode, str, contextNode); 192 } 193 194 /** 195 * Evaluate XPath string to an XObject. 196 * XPath namespace prefixes are resolved from the namespaceNode. 197 * The implementation of this is a little slow, since it creates 198 * a number of objects each time it is called. This could be optimized 199 * to keep the same objects around, but then thread-safety issues would arise. 200 * 201 * @param contextNode The node to start searching from. 202 * @param str A valid XPath string. 203 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 204 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 205 * @see org.apache.xpath.objects.XObject 206 * @see org.apache.xpath.objects.XNull 207 * @see org.apache.xpath.objects.XBoolean 208 * @see org.apache.xpath.objects.XNumber 209 * @see org.apache.xpath.objects.XString 210 * @see org.apache.xpath.objects.XRTreeFrag 211 * 212 * @throws TransformerException 213 */ 214 public static XObject eval(Node contextNode, String str, Node namespaceNode) 215 throws TransformerException 216 { 217 218 // Since we don't have a XML Parser involved here, install some default support 219 // for things like namespaces, etc. 220 // (Changed from: XPathContext xpathSupport = new XPathContext(); 221 // because XPathContext is weak in a number of areas... perhaps 222 // XPathContext should be done away with.) 223 // Create an XPathContext that doesn't support pushing and popping of 224 // variable resolution scopes. Sufficient for simple XPath 1.0 expressions. 225 XPathContext xpathSupport = new XPathContext(false); 226 227 // Create an object to resolve namespace prefixes. 228 // XPath namespaces are resolved from the input context node's document element 229 // if it is a root node, or else the current context node (for lack of a better 230 // resolution space, given the simplicity of this sample code). 231 PrefixResolverDefault prefixResolver = new PrefixResolverDefault( 232 (namespaceNode.getNodeType() == Node.DOCUMENT_NODE) 233 ? ((Document) namespaceNode).getDocumentElement() : namespaceNode); 234 235 // Create the XPath object. 236 XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null); 237 238 // Execute the XPath, and have it return the result 239 // return xpath.execute(xpathSupport, contextNode, prefixResolver); 240 int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode); 241 242 return xpath.execute(xpathSupport, ctxtNode, prefixResolver); 243 } 244 245 /** 246 * Evaluate XPath string to an XObject. 247 * XPath namespace prefixes are resolved from the namespaceNode. 248 * The implementation of this is a little slow, since it creates 249 * a number of objects each time it is called. This could be optimized 250 * to keep the same objects around, but then thread-safety issues would arise. 251 * 252 * @param contextNode The node to start searching from. 253 * @param str A valid XPath string. 254 * @param prefixResolver Will be called if the parser encounters namespace 255 * prefixes, to resolve the prefixes to URLs. 256 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 257 * @see org.apache.xpath.objects.XObject 258 * @see org.apache.xpath.objects.XNull 259 * @see org.apache.xpath.objects.XBoolean 260 * @see org.apache.xpath.objects.XNumber 261 * @see org.apache.xpath.objects.XString 262 * @see org.apache.xpath.objects.XRTreeFrag 263 * 264 * @throws TransformerException 265 */ 266 public static XObject eval( 267 Node contextNode, String str, PrefixResolver prefixResolver) 268 throws TransformerException 269 { 270 271 // Since we don't have a XML Parser involved here, install some default support 272 // for things like namespaces, etc. 273 // (Changed from: XPathContext xpathSupport = new XPathContext(); 274 // because XPathContext is weak in a number of areas... perhaps 275 // XPathContext should be done away with.) 276 // Create the XPath object. 277 XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null); 278 279 // Create an XPathContext that doesn't support pushing and popping of 280 // variable resolution scopes. Sufficient for simple XPath 1.0 expressions. 281 XPathContext xpathSupport = new XPathContext(false); 282 283 // Execute the XPath, and have it return the result 284 int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode); 285 286 return xpath.execute(xpathSupport, ctxtNode, prefixResolver); 287 } 288 } 289