Coverage Report - org.jaxen.dom.DocumentNavigator

Classes in this Package Line Coverage Branch Coverage Complexity
DocumentNavigator
81% 
84% 
2.186

 1  
 package org.jaxen.dom;
 2  
 
 3  
 /*
 4  
  * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/dom/DocumentNavigator.java,v 1.38 2005/05/10 11:48:44 elharo Exp $
 5  
  * $Revision: 1.38 $
 6  
  * $Date: 2005/05/10 11:48:44 $
 7  
  *
 8  
  * ====================================================================
 9  
  *
 10  
  * Copyright (C) 2000-2005 bob mcwhirter & James Strachan.
 11  
  * All rights reserved.
 12  
  *
 13  
  * Redistribution and use in source and binary forms, with or without
 14  
  * modification, are permitted provided that the following conditions
 15  
  * are met:
 16  
  *
 17  
  * 1. Redistributions of source code must retain the above copyright
 18  
  *    notice, this list of conditions, and the following disclaimer.
 19  
  *
 20  
  * 2. Redistributions in binary form must reproduce the above copyright
 21  
  *    notice, this list of conditions, and the disclaimer that follows
 22  
  *    these conditions in the documentation and/or other materials
 23  
  *    provided with the distribution.
 24  
  *
 25  
  * 3. The name "Jaxen" must not be used to endorse or promote products
 26  
  *    derived from this software without prior written permission.  For
 27  
  *    written permission, please contact license@jaxen.org.
 28  
  *
 29  
  * 4. Products derived from this software may not be called "Jaxen", nor
 30  
  *    may "Jaxen" appear in their name, without prior written permission
 31  
  *    from the Jaxen Project Management (pm@jaxen.org).
 32  
  *
 33  
  * In addition, we request (but do not require) that you include in the
 34  
  * end-user documentation provided with the redistribution and/or in the
 35  
  * software itself an acknowledgement equivalent to the following:
 36  
  *     "This product includes software developed by the
 37  
  *      Jaxen Project (http://www.jaxen.org/)."
 38  
  * Alternatively, the acknowledgment may be graphical using the logos
 39  
  * available at http://www.jaxen.org/
 40  
  *
 41  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 42  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 43  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 44  
  * DISCLAIMED.  IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
 45  
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 46  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 47  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 48  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 49  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 50  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 51  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 52  
  * SUCH DAMAGE.
 53  
  *
 54  
  * ====================================================================
 55  
  * This software consists of voluntary contributions made by many
 56  
  * individuals on behalf of the Jaxen Project and was originally
 57  
  * created by bob mcwhirter <bob@werken.com> and
 58  
  * James Strachan <jstrachan@apache.org>.  For more information on the
 59  
  * Jaxen Project, please see <http://www.jaxen.org/>.
 60  
  *
 61  
  * $Id: DocumentNavigator.java,v 1.38 2005/05/10 11:48:44 elharo Exp $
 62  
 */
 63  
 
 64  
 import javax.xml.parsers.DocumentBuilder;
 65  
 import javax.xml.parsers.DocumentBuilderFactory;
 66  
 import java.util.HashMap;
 67  
 import java.util.Iterator;
 68  
 import java.util.NoSuchElementException;
 69  
 
 70  
 import org.jaxen.DefaultNavigator;
 71  
 import org.jaxen.FunctionCallException;
 72  
 import org.jaxen.Navigator;
 73  
 import org.jaxen.XPath;
 74  
 import org.jaxen.JaxenConstants;
 75  
 import org.w3c.dom.Attr;
 76  
 import org.w3c.dom.Document;
 77  
 import org.w3c.dom.NamedNodeMap;
 78  
 import org.w3c.dom.Node;
 79  
 import org.w3c.dom.NodeList;
 80  
 import org.w3c.dom.ProcessingInstruction;
 81  
 
 82  
 /** Interface for navigating around the W3C DOM Level 2 object model.
 83  
  *
 84  
  *  <p>
 85  
  *  This class is not intended for direct usage, but is
 86  
  *  used by the Jaxen engine during evaluation.
 87  
  *  </p>
 88  
  *
 89  
  *  <p>This class implements the {@link org.jaxen.DefaultNavigator} interface
 90  
  *  for the Jaxen XPath library.  This adapter allows the Jaxen
 91  
  *  library to be used to execute XPath queries against any object tree
 92  
  *  that implements the DOM level 2 interfaces.</p>
 93  
  *
 94  
  *  <p>Note: DOM level 2 does not include a node representing an XPath
 95  
  *  namespace node.  This navigator will return namespace nodes
 96  
  *  as instances of the custom {@link NamespaceNode} class, and
 97  
  *  users will have to check result sets to locate and isolate
 98  
  *  these.</p>
 99  
  *
 100  
  *  @author David Megginson
 101  
  *  @author James Strachan
 102  
  *
 103  
  *  @see XPath
 104  
  *  @see NamespaceNode
 105  
  */
 106  
 public class DocumentNavigator extends DefaultNavigator
 107  
 {
 108  
 
 109  
     
 110  
     ////////////////////////////////////////////////////////////////////
 111  
     // Constants.
 112  
     ////////////////////////////////////////////////////////////////////
 113  
 
 114  
     /**
 115  
      * Constant: navigator.
 116  
      */
 117  33
     private final static DocumentNavigator SINGLETON = new DocumentNavigator();
 118  
 
 119  
 
 120  
     
 121  
     ////////////////////////////////////////////////////////////////////
 122  
     // Constructor.
 123  
     ////////////////////////////////////////////////////////////////////
 124  
 
 125  
 
 126  
     /**
 127  
      * Default constructor.
 128  
      */
 129  
     public DocumentNavigator ()
 130  187
     {
 131  187
     }
 132  
 
 133  
 
 134  
     /**
 135  
      * Get a constant DocumentNavigator for efficiency.
 136  
      *
 137  
      * @return a constant instance of a DocumentNavigator.
 138  
      */
 139  
     public static Navigator getInstance ()
 140  
     {
 141  203
         return SINGLETON;
 142  
     }
 143  
 
 144  
 
 145  
     
 146  
     ////////////////////////////////////////////////////////////////////
 147  
     // Implementation of org.jaxen.DefaultNavigator.
 148  
     ////////////////////////////////////////////////////////////////////
 149  
 
 150  
 
 151  
     /**
 152  
      * Get an iterator over all of this node's children.
 153  
      *
 154  
      * @param contextNode the context node for the child axis.
 155  
      * @return a possibly-empty iterator (not null)
 156  
      */
 157  
     public Iterator getChildAxisIterator (Object contextNode)
 158  
     {
 159  117710
         return new NodeIterator ((Node)contextNode) {
 160  
                 protected Node getFirstNode (Node node)
 161  
                 {
 162  117710
                     return node.getFirstChild();
 163  
                 }
 164  117710
                 protected Node getNextNode (Node node)
 165  
                 {
 166  122192
                     return node.getNextSibling();
 167  
                 }
 168  
             };
 169  
     }
 170  
 
 171  
 
 172  
     /**
 173  
      * Get a (single-member) iterator over this node's parent.
 174  
      *
 175  
      * @param contextNode the context node for the parent axis
 176  
      * @return a possibly-empty iterator (not null)
 177  
      */
 178  
     public Iterator getParentAxisIterator (Object contextNode)
 179  
     {
 180  24132
         Node node = (Node)contextNode;
 181  
 
 182  24132
         if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
 183  127
             return new NodeIterator (node) {
 184  
                     protected Node getFirstNode (Node n)
 185  
                     {
 186  
                         // FIXME: assumes castability.
 187  127
                         return ((Attr)n).getOwnerElement();
 188  
                     }
 189  127
                     protected Node getNextNode (Node n) {
 190  127
                         return null;
 191  
                     }
 192  
                 };
 193  
         } else {
 194  24005
             return new NodeIterator (node) {
 195  
                     protected Node getFirstNode (Node n)
 196  
                     {
 197  24005
                         return n.getParentNode();
 198  
                     }
 199  24005
                     protected Node getNextNode (Node n) {
 200  18817
                         return null;
 201  
                     }
 202  
                 };
 203  
         }
 204  
     }
 205  
 
 206  
 
 207  
     /**
 208  
      * Get an iterator over all following siblings.
 209  
      *
 210  
      * @param contextNode the context node for the sibling iterator
 211  
      * @return a possibly-empty iterator (not null)
 212  
      */
 213  
     public Iterator getFollowingSiblingAxisIterator (Object contextNode)
 214  
     {
 215  2372
         return new NodeIterator ((Node)contextNode) {
 216  
                 protected Node getFirstNode (Node node)
 217  
                 {
 218  2372
                     return getNextNode(node);
 219  
                 }
 220  2372
                 protected Node getNextNode (Node node) {
 221  11324
                     return node.getNextSibling();
 222  
                 }
 223  
             };
 224  
     }
 225  
 
 226  
 
 227  
     /**
 228  
      * Get an iterator over all preceding siblings.
 229  
      *
 230  
      * @param contextNode the context node for the preceding sibling axis
 231  
      * @return a possibly-empty iterator (not null)
 232  
      */
 233  
     public Iterator getPrecedingSiblingAxisIterator (Object contextNode)
 234  
     {
 235  12
         return new NodeIterator ((Node)contextNode) {
 236  
                 protected Node getFirstNode (Node node)
 237  
                 {
 238  12
                     return getNextNode(node);
 239  
                 }
 240  12
                 protected Node getNextNode (Node node) {
 241  64
                     return node.getPreviousSibling();
 242  
                 }
 243  
             };
 244  
     }
 245  
 
 246  
 
 247  
     /**
 248  
      * Get an iterator over all following nodes, depth-first.
 249  
      *
 250  
      * @param contextNode the context node for the following axis
 251  
      * @return a possibly-empty iterator (not null)
 252  
      */
 253  
     public Iterator getFollowingAxisIterator (Object contextNode)
 254  
     {
 255  8
         return new NodeIterator ((Node)contextNode) {
 256  
                 protected Node getFirstNode (Node node)
 257  
                 {
 258  61
                     if (node == null)
 259  8
                         return null;
 260  
                     else {
 261  53
                         Node sibling = node.getNextSibling();
 262  53
                         if (sibling == null)
 263  17
                             return getFirstNode(node.getParentNode());
 264  
                         else
 265  36
                             return sibling;
 266  
                     }
 267  
                 }
 268  8
                 protected Node getNextNode (Node node) {
 269  96
                     if (node == null)
 270  0
                         return null;
 271  
                     else {
 272  96
                         Node n = node.getFirstChild();
 273  96
                         if (n == null)
 274  70
                             n = node.getNextSibling();
 275  96
                         if (n == null)
 276  36
                             return getFirstNode(node.getParentNode());
 277  
                         else
 278  60
                             return n;
 279  
                     }
 280  
                 }
 281  
             };
 282  
     }
 283  
 
 284  
 
 285  
     /**
 286  
      * Get an iterator over all attributes.
 287  
      *
 288  
      * @param contextNode the context node for the attribute axis
 289  
      * @return a possibly-empty iterator (not null)
 290  
      */
 291  
     public Iterator getAttributeAxisIterator (Object contextNode)
 292  
     {
 293  733
         if (isElement(contextNode)) {
 294  685
             return new AttributeIterator((Node)contextNode);
 295  
         } else {
 296  48
             return JaxenConstants.EMPTY_ITERATOR;
 297  
         }
 298  
     }
 299  
 
 300  
 
 301  
     /**
 302  
      * Get an iterator over all declared namespaces.
 303  
      *
 304  
      * <p>Note: this iterator is not live: it takes a snapshot
 305  
      * and that snapshot remains static during the life of
 306  
      * the iterator (i.e. it won't reflect subsequent changes
 307  
      * to the DOM).</p>
 308  
      *
 309  
      * @param contextNode the context node for the namespace axis
 310  
      * @return a possibly-empty iterator (not null)
 311  
      */
 312  
     public Iterator getNamespaceAxisIterator (Object contextNode)
 313  
     {
 314  
         // Only elements have namespace nodes
 315  116
         if (isElement(contextNode)) {
 316  
 
 317  55
             HashMap nsMap = new HashMap();
 318  
 
 319  
             // Start at the current node at walk
 320  
             // up to the root, noting what namespace
 321  
             // declarations are in force.
 322  
 
 323  
             // TODO: deal with empty URI for
 324  
             // canceling namespace scope
 325  55
             for (Node n = (Node)contextNode;
 326  242
                  n != null;
 327  187
                  n = n.getParentNode()) {
 328  187
                 if (n.hasAttributes()) {
 329  44
                     NamedNodeMap atts = n.getAttributes();
 330  44
                     int length = atts.getLength();
 331  181
                     for (int i = 0; i < length; i++) {
 332  137
                         Node att = atts.item(i);
 333  137
                         if (att.getNodeName().startsWith("xmlns")) {
 334  78
                             NamespaceNode ns =
 335  
                                 new NamespaceNode((Node)contextNode, att);
 336  
                             // Add only if there's not a closer
 337  
                             // declaration in force.
 338  78
                             String name = ns.getNodeName();
 339  78
                             if (!nsMap.containsKey(name))
 340  78
                                 nsMap.put(name, ns);
 341  
                         }
 342  
                     }
 343  
                 }
 344  
             }
 345  
             // Section 5.4 of the XPath rec requires
 346  
             // this to be present.
 347  55
             nsMap.put("xml",
 348  
                       new
 349  
                       NamespaceNode((Node)contextNode,
 350  
                                     "xml",
 351  
                                     "http://www.w3.org/XML/1998/namespace"));
 352  
 
 353  
             // An empty default namespace cancels
 354  
             // any previous default.
 355  55
             NamespaceNode defaultNS = (NamespaceNode)nsMap.get("");
 356  55
             if (defaultNS != null && defaultNS.getNodeValue().length() == 0)
 357  0
                 nsMap.remove("");
 358  55
             return nsMap.values().iterator();
 359  
         } else {
 360  61
             return JaxenConstants.EMPTY_ITERATOR;
 361  
         }
 362  
     }
 363  
 
 364  
     /** Returns a parsed form of the given XPath string, which will be suitable
 365  
      *  for queries on DOM documents.
 366  
      */
 367  
     public XPath parseXPath (String xpath) throws org.jaxen.saxpath.SAXPathException
 368  
     {
 369  7
         return new DOMXPath(xpath);
 370  
     }
 371  
 
 372  
     /**
 373  
      * Get the top-level document node.
 374  
      *
 375  
      * @param contextNode any node in the document
 376  
      * @return the root node
 377  
      */
 378  
     public Object getDocumentNode (Object contextNode)
 379  
     {
 380  344
         if (isDocument(contextNode)) return contextNode;
 381  68
         else return ((Node)contextNode).getOwnerDocument();
 382  
     }
 383  
 
 384  
 
 385  
     /**
 386  
      * Get the namespace URI of an element.
 387  
      *
 388  
      * @param object the target node
 389  
      * @return a string (possibly empty) if the node is an element,
 390  
      * and null otherwise
 391  
      */
 392  
     public String getElementNamespaceUri (Object object)
 393  
     {
 394  40209
         String uri = ((Node)object).getNamespaceURI();
 395  40209
         return uri;
 396  
     }
 397  
 
 398  
 
 399  
     /**
 400  
      * Get the local name of an element.
 401  
      *
 402  
      * @param object the target node
 403  
      * @return a string representing the unqualified local name
 404  
      * if the node is an element, or null otherwise
 405  
      */
 406  
     public String getElementName (Object object)
 407  
     {
 408  40441
         String name = ((Node)object).getLocalName();
 409  40441
         if (name == null) name = ((Node)object).getNodeName();
 410  40441
         return name;
 411  
     }
 412  
 
 413  
 
 414  
     /**
 415  
      * Get the qualified name of an element.
 416  
      *
 417  
      * @param object the target node
 418  
      * @return a string representing the qualified (i.e. possibly
 419  
      * prefixed) name if the node is an element, or null otherwise
 420  
      */
 421  
     public String getElementQName (Object object)
 422  
     {
 423  29
         String qname = ((Node)object).getNodeName();
 424  29
         if (qname == null) qname = ((Node)object).getLocalName();
 425  29
         return qname;
 426  
     }
 427  
 
 428  
 
 429  
     /**
 430  
      * Get the Namespace URI of an attribute.
 431  
      *
 432  
      * @param object the target node
 433  
      */
 434  
     public String getAttributeNamespaceUri (Object object)
 435  
     {
 436  504
         String uri = ((Node)object).getNamespaceURI();
 437  504
         return uri;
 438  
     }
 439  
 
 440  
 
 441  
     /**
 442  
      * Get the local name of an attribute.
 443  
      *
 444  
      * @param object the target node
 445  
      * @return a string representing the unqualified local name
 446  
      * if the node is an attribute, or null otherwise
 447  
      */
 448  
     public String getAttributeName (Object object)
 449  
     {
 450  504
         String name = ((Node)object).getLocalName();
 451  504
         if (name == null) name = ((Node)object).getNodeName();
 452  504
         return name;
 453  
     }
 454  
 
 455  
 
 456  
     /**
 457  
      * Get the qualified name of an attribute.
 458  
      *
 459  
      * @param object the target node
 460  
      * @return a string representing the qualified (i.e. possibly
 461  
      * prefixed) name if the node is an attribute, or null otherwise
 462  
      */
 463  
     public String getAttributeQName (Object object)
 464  
     {
 465  1
         String qname = ((Node)object).getNodeName();
 466  1
         if (qname == null) qname = ((Node)object).getLocalName();
 467  1
         return qname;
 468  
     }
 469  
 
 470  
 
 471  
     /**
 472  
      * Test if a node is a top-level document.
 473  
      *
 474  
      * @param object the target node
 475  
      * @return true if the node is the document root, false otherwise
 476  
      */
 477  
     public boolean isDocument (Object object)
 478  
     {
 479  1865
         return (object instanceof Node) &&
 480  
             (((Node)object).getNodeType() == Node.DOCUMENT_NODE);
 481  
     }
 482  
 
 483  
 
 484  
     /**
 485  
      * Test if a node is a namespace.
 486  
      *
 487  
      * @param object the target node
 488  
      * @return true if the node is a namespace, false otherwise
 489  
      */
 490  
     public boolean isNamespace (Object object)
 491  
     {
 492  4405
         return (object instanceof NamespaceNode);
 493  
     }
 494  
 
 495  
 
 496  
     /**
 497  
      * Test if a node is an element.
 498  
      *
 499  
      * @param object the target node
 500  
      * @return true if the node is an element, false otherwise
 501  
      */
 502  
     public boolean isElement (Object object)
 503  
     {
 504  120715
         return (object instanceof Node) &&
 505  
             (((Node)object).getNodeType() == Node.ELEMENT_NODE);
 506  
     }
 507  
 
 508  
 
 509  
     /**
 510  
      * Test if a node is an attribute. <code>xmlns</code> and 
 511  
      * <code>xmlns:pre</code> attributes do not count as attributes
 512  
      * for the purposes of XPath. 
 513  
      *
 514  
      * @param object the target node
 515  
      * @return true if the node is an attribute, false otherwise
 516  
      */
 517  
     public boolean isAttribute (Object object)
 518  
     {
 519  5475
         return (object instanceof Node) &&
 520  
             (((Node)object).getNodeType() == Node.ATTRIBUTE_NODE)
 521  
             && ! "http://www.w3.org/2000/xmlns/".equals(((Node) object).getNamespaceURI());
 522  
     }
 523  
 
 524  
 
 525  
     /**
 526  
      * Test if a node is a comment.
 527  
      *
 528  
      * @param object the target node
 529  
      * @return true if the node is a comment, false otherwise
 530  
      */
 531  
     public boolean isComment (Object object)
 532  
     {
 533  1195
         return (object instanceof Node) &&
 534  
             (((Node)object).getNodeType() == Node.COMMENT_NODE);
 535  
     }
 536  
 
 537  
 
 538  
     /**
 539  
      * Test if a node is plain text.
 540  
      *
 541  
      * @param object the target node
 542  
      * @return true if the node is a text node, false otherwise
 543  
      */
 544  
     public boolean isText (Object object)
 545  
     {
 546  80490
         if (object instanceof Node) {
 547  78316
             switch (((Node)object).getNodeType()) {
 548  
                 case Node.TEXT_NODE:
 549  
                 case Node.CDATA_SECTION_NODE:
 550  77303
                     return true;
 551  
                 default:
 552  1013
                     return false;
 553  
             }
 554  
         } else {
 555  2174
             return false;
 556  
         }
 557  
     }
 558  
 
 559  
 
 560  
     /**
 561  
      * Test if a node is a processing instruction.
 562  
      *
 563  
      * @param object the target node
 564  
      * @return true if the node is a processing instruction, false otherwise
 565  
      */
 566  
     public boolean isProcessingInstruction (Object object)
 567  
     {
 568  1174
         return (object instanceof Node) &&
 569  
             (((Node)object).getNodeType() == Node.PROCESSING_INSTRUCTION_NODE);
 570  
     }
 571  
 
 572  
 
 573  
     /**
 574  
      * Get the string value of an element node.
 575  
      *
 576  
      * @param object the target node
 577  
      * @return the text inside the node and its descendants if the node
 578  
      * is an element, null otherwise
 579  
      */
 580  
     public String getElementStringValue (Object object)
 581  
     {
 582  0
         if (isElement(object)) {
 583  0
             return getStringValue((Node)object, new StringBuffer()).toString();
 584  
         }
 585  
         else {
 586  0
             return null;
 587  
         }
 588  
     }
 589  
 
 590  
 
 591  
     /**
 592  
      * Construct an element's string value recursively.
 593  
      *
 594  
      * @param node the current node
 595  
      * @param buffer the buffer for building the text
 596  
      * @return the buffer passed as a parameter (for convenience)
 597  
      */
 598  
     private StringBuffer getStringValue (Node node, StringBuffer buffer)
 599  
     {
 600  0
         if (isText(node)) {
 601  0
             buffer.append(node.getNodeValue());
 602  
         } else {
 603  0
             NodeList children = node.getChildNodes();
 604  0
             int length = children.getLength();
 605  0
             for (int i = 0; i < length; i++) {
 606  0
                 getStringValue(children.item(i), buffer);
 607  
             }
 608  
         }
 609  0
         return buffer;
 610  
     }
 611  
 
 612  
 
 613  
     /**
 614  
      * Get the string value of an attribute node.
 615  
      *
 616  
      * @param object the target node
 617  
      * @return the text of the attribute value if the node is an
 618  
      * attribute, null otherwise
 619  
      */
 620  
     public String getAttributeStringValue (Object object)
 621  
     {
 622  310
         if (isAttribute(object)) return ((Node)object).getNodeValue();
 623  0
         else return null;
 624  
     }
 625  
 
 626  
 
 627  
     /**
 628  
      * Get the string value of text.
 629  
      *
 630  
      * @param object the target node
 631  
      * @return the string of text if the node is text, null otherwise
 632  
      */
 633  
     public String getTextStringValue (Object object)
 634  
     {
 635  150
         if (isText(object)) return ((Node)object).getNodeValue();
 636  0
         else return null;
 637  
     }
 638  
 
 639  
 
 640  
     /**
 641  
      * Get the string value of a comment node.
 642  
      *
 643  
      * @param object the target node
 644  
      * @return the text of the comment if the node is a comment,
 645  
      * null otherwise
 646  
      */
 647  
     public String getCommentStringValue (Object object)
 648  
     {
 649  0
         if (isComment(object)) return ((Node)object).getNodeValue();
 650  0
         else return null;
 651  
     }
 652  
 
 653  
 
 654  
     /**
 655  
      * Get the string value of a namespace node.
 656  
      *
 657  
      * @param object the target node
 658  
      * @return the namespace URI as a (possibly empty) string if the
 659  
      * node is a namespace node, null otherwise
 660  
      */
 661  
     public String getNamespaceStringValue (Object object)
 662  
     {
 663  0
         if (isNamespace(object)) return ((NamespaceNode)object).getNodeValue();
 664  0
         else return null;
 665  
     }
 666  
 
 667  
     /**
 668  
      * Get the prefix value of a Namespace node.
 669  
      *
 670  
      * @param object the target node
 671  
      * @return the Namespace prefix a (possibly empty) string if the
 672  
      * node is a namespace node, null otherwise
 673  
      */
 674  
     public String getNamespacePrefix (Object object)
 675  
     {
 676  119
         if (isNamespace(object)) return ((NamespaceNode)object).getLocalName();
 677  0
         else return null;
 678  
     }
 679  
 
 680  
     /**
 681  
      * Translate a Namespace prefix to a URI.
 682  
      */
 683  
     public String translateNamespacePrefixToUri (String prefix, Object element)
 684  
     {
 685  0
         Iterator it = getNamespaceAxisIterator(element);
 686  0
         while (it.hasNext()) {
 687  0
             NamespaceNode ns = (NamespaceNode)it.next();
 688  0
             if (prefix.equals(ns.getNodeName())) return ns.getNodeValue();
 689  
         }
 690  0
         return null;
 691  
     }
 692  
 
 693  
     /**
 694  
      * Use JAXP to load a namespace aware document from a given URI
 695  
      *
 696  
      * @param uri is the URI of the document to load
 697  
      * @return the new W3C DOM Level 2 Document instance
 698  
      * @throws FunctionCallException containing a nested exception
 699  
      *      if a problem occurs trying to parse the given document
 700  
      */
 701  
     public Object getDocument(String uri) throws FunctionCallException
 702  
     {
 703  
         try
 704  
         {
 705  4
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 706  4
             factory.setNamespaceAware(true);
 707  4
             DocumentBuilder builder = factory.newDocumentBuilder();
 708  4
             return builder.parse( uri );
 709  
         }
 710  0
         catch (Exception e)
 711  
         {
 712  0
             throw new FunctionCallException("Failed to parse document for URI: " + uri, e);
 713  
         }
 714  
     }
 715  
 
 716  
     public String getProcessingInstructionTarget(Object obj)
 717  
     {
 718  8
         ProcessingInstruction pi = (ProcessingInstruction) obj;
 719  
 
 720  8
         return pi.getTarget();
 721  
     }
 722  
 
 723  
     public String getProcessingInstructionData(Object obj)
 724  
     {
 725  3
         ProcessingInstruction pi = (ProcessingInstruction) obj;
 726  
 
 727  3
         return pi.getData();
 728  
     }
 729  
 
 730  
     
 731  
     ////////////////////////////////////////////////////////////////////
 732  
     // Inner class: iterate over DOM nodes.
 733  
     ////////////////////////////////////////////////////////////////////
 734  
 
 735  
 
 736  
     // FIXME: needs to recurse into
 737  
     // DocumentFragment and EntityReference
 738  
     // to use their children.
 739  
 
 740  
     /**
 741  
      * A generic iterator over DOM nodes.
 742  
      *
 743  
      * <p>Concrete subclasses must implement the {@link #getFirstNode}
 744  
      * and {@link #getNextNode} methods for a specific iteration
 745  
      * strategy.</p>
 746  
      */
 747  
     abstract class NodeIterator
 748  
     implements Iterator
 749  
     {
 750  
 
 751  
 
 752  
         /**
 753  
          * Constructor.
 754  
          *
 755  
          * @param contextNode the starting node
 756  
          */
 757  
         public NodeIterator (Node contextNode)
 758  144234
         {
 759  144234
             node = getFirstNode(contextNode);
 760  144240
             while (!isXPathNode(node))
 761  6
                 node = getNextNode(node);
 762  144234
         }
 763  
 
 764  
 
 765  
         /**
 766  
          * @see Iterator#hasNext
 767  
          */
 768  
         public boolean hasNext ()
 769  
         {
 770  387683
             return (node != null);
 771  
         }
 772  
 
 773  
 
 774  
         /**
 775  
          * @see Iterator#next
 776  
          */
 777  
         public Object next ()
 778  
         {
 779  150230
             if (node == null) throw new NoSuchElementException();
 780  150230
             Node ret = node;
 781  150230
             node = getNextNode(node);
 782  150230
             while (!isXPathNode(node)) {
 783  0
                 node = getNextNode(node);
 784  
             }
 785  150230
             return ret;
 786  
         }
 787  
 
 788  
 
 789  
         /**
 790  
          * @see Iterator#remove
 791  
          */
 792  
         public void remove ()
 793  
         {
 794  0
             throw new UnsupportedOperationException();
 795  
         }
 796  
 
 797  
 
 798  
         /**
 799  
          * Get the first node for iteration.
 800  
          *
 801  
          * <p>This method must derive an initial node for iteration
 802  
          * from a context node.</p>
 803  
          *
 804  
          * @param contextNode the starting node
 805  
          * @return the first node in the iteration
 806  
          * @see #getNextNode
 807  
          */
 808  
         protected abstract Node getFirstNode (Node contextNode);
 809  
 
 810  
 
 811  
         /**
 812  
          * Get the next node for iteration.
 813  
          *
 814  
          * <p>This method must locate a following node from the
 815  
          * current context node.</p>
 816  
          *
 817  
          * @param contextNode the current node in the iteration
 818  
          * @return the following node in the iteration, or null
 819  
          * if there is none
 820  
          * @see #getFirstNode
 821  
          */
 822  
         protected abstract Node getNextNode (Node contextNode);
 823  
 
 824  
 
 825  
         /**
 826  
          * Test whether a DOM node is usable by XPath.
 827  
          *
 828  
          * @param node the DOM node to test
 829  
          * @return true if the node is usable, false if it should be
 830  
          * skipped
 831  
          */
 832  
         private boolean isXPathNode (Node node)
 833  
         {
 834  
             // null is usable, because it means end
 835  294470
             if (node == null)
 836  142123
                 return true;
 837  
 
 838  152347
             switch (node.getNodeType()) {
 839  
                 case Node.DOCUMENT_FRAGMENT_NODE:
 840  
                 case Node.DOCUMENT_TYPE_NODE:
 841  
                 case Node.ENTITY_NODE:
 842  
                 case Node.ENTITY_REFERENCE_NODE:
 843  
                 case Node.NOTATION_NODE:
 844  6
                     return false;
 845  
                 default:
 846  152341
                     return true;
 847  
             }
 848  
         }
 849  
 
 850  
         private Node node;
 851  
     }
 852  
 
 853  
 
 854  
     
 855  
     ////////////////////////////////////////////////////////////////////
 856  
     // Inner class: iterate over a DOM named node map.
 857  
     ////////////////////////////////////////////////////////////////////
 858  
 
 859  
 
 860  
     /**
 861  
      * An iterator over an attribute list.
 862  
      */
 863  
     private static class AttributeIterator implements Iterator
 864  
     {
 865  
 
 866  
         /**
 867  
          * Constructor.
 868  
          *
 869  
          * @param parent the parent DOM element for the attributes.
 870  
          */
 871  
         AttributeIterator (Node parent)
 872  685
         {
 873  685
             this.map = parent.getAttributes();
 874  685
             this.pos = 0;
 875  685
         }
 876  
 
 877  
 
 878  
         /**
 879  
          * @see Iterator#hasNext
 880  
          */
 881  
         public boolean hasNext ()
 882  
         {
 883  1480
             return pos < map.getLength();
 884  
         }
 885  
 
 886  
 
 887  
         /**
 888  
          * @see Iterator#next
 889  
          */
 890  
         public Object next ()
 891  
         {
 892  504
             Node attr = map.item(pos++);
 893  504
             if (attr == null) throw new NoSuchElementException();
 894  504
             else return attr;
 895  
         }
 896  
 
 897  
 
 898  
         /**
 899  
          * @see Iterator#remove
 900  
          */
 901  
         public void remove ()
 902  
         {
 903  0
             throw new UnsupportedOperationException();
 904  
         }
 905  
 
 906  
 
 907  
         private NamedNodeMap map;
 908  
         private int pos;
 909  
 
 910  
     }
 911  
 
 912  
     /**
 913  
      *  Returns the element whose ID is given by elementId.
 914  
      *  If no such element exists, returns null.
 915  
      *  Attributes with the name "ID" are not of type ID unless so defined.
 916  
      *  Attribute types are only known if when the parser understands DTD's or
 917  
      *  schemas that declare attributes of type ID. When JAXP is used, you
 918  
      *  must call <code>setValidating(true)</code> on the
 919  
      *  DocumentBuilderFactory.
 920  
      *
 921  
      *  @param object   a node from the document in which to look for the id
 922  
      *  @param elementId   id to look for
 923  
      *
 924  
      *  @return   element whose ID is given by elementId, or null if no such
 925  
      *            element exists in the document or if the implementation
 926  
      *            does not know about attribute types
 927  
      *  @see   javax.xml.parsers.DocumentBuilderFactory
 928  
      */
 929  
     public Object getElementById(Object object, String elementId)
 930  
     {
 931  4
         Document doc = (Document)getDocumentNode(object);
 932  4
         if (doc != null) return doc.getElementById(elementId);
 933  0
         else return null;
 934  
     }
 935  
 
 936  
 }
 937  
 
 938  
 // end of DocumentNavigator.java