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 }