1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 package com.sun.org.apache.xml.internal.security.utils; 22 23 24 25 import javax.xml.transform.TransformerException; 26 27 import com.sun.org.apache.xml.internal.security.transforms.implementations.FuncHereContext; 28 import com.sun.org.apache.xml.internal.utils.PrefixResolver; 29 import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault; 30 import com.sun.org.apache.xpath.internal.XPath; 31 import com.sun.org.apache.xpath.internal.objects.XObject; 32 import org.w3c.dom.Attr; 33 import org.w3c.dom.Document; 34 import org.w3c.dom.Node; 35 import org.w3c.dom.NodeList; 36 import org.w3c.dom.ProcessingInstruction; 37 import org.w3c.dom.Text; 38 import org.w3c.dom.traversal.NodeIterator; 39 40 41 42 43 /** 44 * This class does the same as {@link com.sun.org.apache.xpath.internal.XPathAPI} except that the XPath strings 45 * are not supplied as Strings but as {@link Text}, {@link Attr}ibute or 46 * {ProcessingInstruction} nodes which contain the XPath string. This enables 47 * us to use the <CODE>here()</CODE> function. 48 * <BR> 49 * The methods in this class are convenience methods into the low-level XPath API. 50 * These functions tend to be a little slow, since a number of objects must be 51 * created for each evaluation. A faster way is to precompile the 52 * XPaths using the low-level API, and then just use the XPaths 53 * over and over. 54 * 55 * @author $Author: mullan $ 56 * @see <a href="http://www.w3.org/TR/xpath">XPath Specification</a> 57 */ 58 public class XPathFuncHereAPI { 59 60 /** 61 * Use an XPath string to select a single node. XPath namespace 62 * prefixes are resolved from the context node, which may not 63 * be what you want (see the next method). 64 * 65 * @param contextNode The node to start searching from. 66 * @param xpathnode A Node containing a valid XPath string. 67 * @return The first node found that matches the XPath, or null. 68 * 69 * @throws TransformerException 70 */ 71 public static Node selectSingleNode(Node contextNode, Node xpathnode) 72 throws TransformerException { 73 return selectSingleNode(contextNode, xpathnode, contextNode); 74 } 75 76 /** 77 * Use an XPath string to select a single node. 78 * XPath namespace prefixes are resolved from the namespaceNode. 79 * 80 * @param contextNode The node to start searching from. 81 * @param xpathnode 82 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 83 * @return The first node found that matches the XPath, or null. 84 * 85 * @throws TransformerException 86 */ 87 public static Node selectSingleNode( 88 Node contextNode, Node xpathnode, Node namespaceNode) 89 throws TransformerException { 90 91 // Have the XObject return its result as a NodeSetDTM. 92 NodeIterator nl = selectNodeIterator(contextNode, xpathnode, 93 namespaceNode); 94 95 // Return the first node, or null 96 return nl.nextNode(); 97 } 98 99 /** 100 * Use an XPath string to select a nodelist. 101 * XPath namespace prefixes are resolved from the contextNode. 102 * 103 * @param contextNode The node to start searching from. 104 * @param xpathnode 105 * @return A NodeIterator, should never be null. 106 * 107 * @throws TransformerException 108 */ 109 public static NodeIterator selectNodeIterator( 110 Node contextNode, Node xpathnode) throws TransformerException { 111 return selectNodeIterator(contextNode, xpathnode, contextNode); 112 } 113 114 /** 115 * Use an XPath string to select a nodelist. 116 * XPath namespace prefixes are resolved from the namespaceNode. 117 * 118 * @param contextNode The node to start searching from. 119 * @param xpathnode 120 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 121 * @return A NodeIterator, should never be null. 122 * 123 * @throws TransformerException 124 */ 125 public static NodeIterator selectNodeIterator( 126 Node contextNode, Node xpathnode, Node namespaceNode) 127 throws TransformerException { 128 129 // Execute the XPath, and have it return the result 130 XObject list = eval(contextNode, xpathnode, namespaceNode); 131 132 // Have the XObject return its result as a NodeSetDTM. 133 return list.nodeset(); 134 } 135 136 /** 137 * Use an XPath string to select a nodelist. 138 * XPath namespace prefixes are resolved from the contextNode. 139 * 140 * @param contextNode The node to start searching from. 141 * @param xpathnode 142 * @return A NodeIterator, should never be null. 143 * 144 * @throws TransformerException 145 */ 146 public static NodeList selectNodeList(Node contextNode, Node xpathnode) 147 throws TransformerException { 148 return selectNodeList(contextNode, xpathnode, contextNode); 149 } 150 151 /** 152 * Use an XPath string to select a nodelist. 153 * XPath namespace prefixes are resolved from the namespaceNode. 154 * 155 * @param contextNode The node to start searching from. 156 * @param xpathnode 157 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 158 * @return A NodeIterator, should never be null. 159 * 160 * @throws TransformerException 161 */ 162 public static NodeList selectNodeList( 163 Node contextNode, Node xpathnode, Node namespaceNode) 164 throws TransformerException { 165 166 // Execute the XPath, and have it return the result 167 XObject list = eval(contextNode, xpathnode, 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 xpathnode 178 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 179 * @see com.sun.org.apache.xpath.internal.objects.XObject 180 * @see com.sun.org.apache.xpath.internal.objects.XNull 181 * @see com.sun.org.apache.xpath.internal.objects.XBoolean 182 * @see com.sun.org.apache.xpath.internal.objects.XNumber 183 * @see com.sun.org.apache.xpath.internal.objects.XString 184 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag 185 * 186 * @throws TransformerException 187 */ 188 public static XObject eval(Node contextNode, Node xpathnode) 189 throws TransformerException { 190 return eval(contextNode, xpathnode, contextNode); 191 } 192 193 /** 194 * Evaluate XPath string to an XObject. 195 * XPath namespace prefixes are resolved from the namespaceNode. 196 * The implementation of this is a little slow, since it creates 197 * a number of objects each time it is called. This could be optimized 198 * to keep the same objects around, but then thread-safety issues would arise. 199 * 200 * @param contextNode The node to start searching from. 201 * @param xpathnode 202 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces. 203 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 204 * @see com.sun.org.apache.xpath.internal.objects.XObject 205 * @see com.sun.org.apache.xpath.internal.objects.XNull 206 * @see com.sun.org.apache.xpath.internal.objects.XBoolean 207 * @see com.sun.org.apache.xpath.internal.objects.XNumber 208 * @see com.sun.org.apache.xpath.internal.objects.XString 209 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag 210 * 211 * @throws TransformerException 212 */ 213 public static XObject eval( 214 Node contextNode, Node xpathnode, Node namespaceNode) 215 throws TransformerException { 216 217 // Since we don't have a XML Parser involved here, install some default support 218 // for things like namespaces, etc. 219 // (Changed from: XPathContext xpathSupport = new XPathContext(); 220 // because XPathContext is weak in a number of areas... perhaps 221 // XPathContext should be done away with.) 222 FuncHereContext xpathSupport = new FuncHereContext(xpathnode); 223 224 // Create an object to resolve namespace prefixes. 225 // XPath namespaces are resolved from the input context node's document element 226 // if it is a root node, or else the current context node (for lack of a better 227 // resolution space, given the simplicity of this sample code). 228 PrefixResolverDefault prefixResolver = 229 new PrefixResolverDefault((namespaceNode.getNodeType() 230 == Node.DOCUMENT_NODE) 231 ? ((Document) namespaceNode) 232 .getDocumentElement() 233 : namespaceNode); 234 String str = getStrFromNode(xpathnode); 235 236 // Create the XPath object. 237 XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null); 238 239 // Execute the XPath, and have it return the result 240 // return xpath.execute(xpathSupport, contextNode, prefixResolver); 241 int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode); 242 243 return xpath.execute(xpathSupport, ctxtNode, prefixResolver); 244 } 245 246 /** 247 * Evaluate XPath string to an XObject. 248 * XPath namespace prefixes are resolved from the namespaceNode. 249 * The implementation of this is a little slow, since it creates 250 * a number of objects each time it is called. This could be optimized 251 * to keep the same objects around, but then thread-safety issues would arise. 252 * 253 * @param contextNode The node to start searching from. 254 * @param xpathnode 255 * @param prefixResolver Will be called if the parser encounters namespace 256 * prefixes, to resolve the prefixes to URLs. 257 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null. 258 * @see com.sun.org.apache.xpath.internal.objects.XObject 259 * @see com.sun.org.apache.xpath.internal.objects.XNull 260 * @see com.sun.org.apache.xpath.internal.objects.XBoolean 261 * @see com.sun.org.apache.xpath.internal.objects.XNumber 262 * @see com.sun.org.apache.xpath.internal.objects.XString 263 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag 264 * 265 * @throws TransformerException 266 */ 267 public static XObject eval( 268 Node contextNode, Node xpathnode, PrefixResolver prefixResolver) 269 throws TransformerException { 270 271 String str = getStrFromNode(xpathnode); 272 273 // Since we don't have a XML Parser involved here, install some default support 274 // for things like namespaces, etc. 275 // (Changed from: XPathContext xpathSupport = new XPathContext(); 276 // because XPathContext is weak in a number of areas... perhaps 277 // XPathContext should be done away with.) 278 // Create the XPath object. 279 XPath xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null); 280 281 // Execute the XPath, and have it return the result 282 FuncHereContext xpathSupport = new FuncHereContext(xpathnode); 283 int ctxtNode = xpathSupport.getDTMHandleFromNode(contextNode); 284 285 return xpath.execute(xpathSupport, ctxtNode, prefixResolver); 286 } 287 288 /** 289 * Method getStrFromNode 290 * 291 * @param xpathnode 292 * @return the string from the node 293 */ 294 private static String getStrFromNode(Node xpathnode) { 295 296 if (xpathnode.getNodeType() == Node.TEXT_NODE) { 297 return ((Text) xpathnode).getData(); 298 } else if (xpathnode.getNodeType() == Node.ATTRIBUTE_NODE) { 299 return ((Attr) xpathnode).getNodeValue(); 300 } else if (xpathnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 301 return ((ProcessingInstruction) xpathnode).getNodeValue(); 302 } 303 304 return ""; 305 } 306 }