Coverage Report - org.jaxen.BaseXPath
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseXPath
98%
85/86
92%
24/26
1.926
 
 1  
 /*
 2  
  * $Header$
 3  
  * $Revision$
 4  
  * $Date$
 5  
  *
 6  
  * ====================================================================
 7  
  *
 8  
  * Copyright 2000-2002 bob mcwhirter & James Strachan.
 9  
  * All rights reserved.
 10  
  *
 11  
  * Redistribution and use in source and binary forms, with or without
 12  
  * modification, are permitted provided that the following conditions are
 13  
  * met:
 14  
  * 
 15  
  *   * Redistributions of source code must retain the above copyright
 16  
  *     notice, this list of conditions and the following disclaimer.
 17  
  * 
 18  
  *   * Redistributions in binary form must reproduce the above copyright
 19  
  *     notice, this list of conditions and the following disclaimer in the
 20  
  *     documentation and/or other materials provided with the distribution.
 21  
  * 
 22  
  *   * Neither the name of the Jaxen Project nor the names of its
 23  
  *     contributors may be used to endorse or promote products derived 
 24  
  *     from this software without specific prior written permission.
 25  
  * 
 26  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 27  
  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 28  
  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 29  
  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 30  
  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 31  
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 32  
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 33  
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 34  
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 35  
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 36  
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 37  
  *
 38  
  * ====================================================================
 39  
  * This software consists of voluntary contributions made by many 
 40  
  * individuals on behalf of the Jaxen Project and was originally 
 41  
  * created by bob mcwhirter <bob@werken.com> and 
 42  
  * James Strachan <jstrachan@apache.org>.  For more information on the 
 43  
  * Jaxen Project, please see <http://www.jaxen.org/>.
 44  
  * 
 45  
  * $Id$
 46  
  */
 47  
 
 48  
 
 49  
 package org.jaxen;
 50  
 
 51  
 import java.io.Serializable;
 52  
 import java.util.List;
 53  
 
 54  
 import org.jaxen.expr.Expr;
 55  
 import org.jaxen.expr.XPathExpr;
 56  
 import org.jaxen.function.BooleanFunction;
 57  
 import org.jaxen.function.NumberFunction;
 58  
 import org.jaxen.function.StringFunction;
 59  
 import org.jaxen.saxpath.SAXPathException;
 60  
 import org.jaxen.saxpath.XPathReader;
 61  
 import org.jaxen.saxpath.helpers.XPathReaderFactory;
 62  
 import org.jaxen.util.SingletonList;
 63  
 
 64  
 /** Base functionality for all concrete, implementation-specific XPaths.
 65  
  *
 66  
  *  <p>
 67  
  *  This class provides generic functionality for further-defined
 68  
  *  implementation-specific XPaths.
 69  
  *  </p>
 70  
  *
 71  
  *  <p>
 72  
  *  If you want to adapt the Jaxen engine to traverse your own
 73  
  *  object model, then this is a good base class to derive from.
 74  
  *  Typically you only really need to provide your own 
 75  
  *  {@link org.jaxen.Navigator} implementation.
 76  
  *  </p>
 77  
  *
 78  
  *  @see org.jaxen.dom4j.Dom4jXPath XPath for dom4j
 79  
  *  @see org.jaxen.jdom.JDOMXPath   XPath for JDOM
 80  
  *  @see org.jaxen.dom.DOMXPath     XPath for W3C DOM
 81  
  *
 82  
  *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
 83  
  *  @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 84  
  */
 85  
 public class BaseXPath implements XPath, Serializable
 86  
 {
 87  
 
 88  
     private static final long serialVersionUID = -1993731281300293168L;
 89  
 
 90  
     /** Original expression text. */
 91  
     private final String exprText;
 92  
 
 93  
     /** the parsed form of the XPath expression */
 94  
     private final XPathExpr xpath;
 95  
     
 96  
     /** the support information and function, namespace and variable contexts */
 97  
     private ContextSupport support;
 98  
 
 99  
     /** the implementation-specific Navigator for retrieving XML nodes **/
 100  
     private Navigator navigator;
 101  
     
 102  
     /** Construct given an XPath expression string. 
 103  
      *
 104  
      *  @param xpathExpr the XPath expression
 105  
      *
 106  
      *  @throws JaxenException if there is a syntax error while
 107  
      *          parsing the expression
 108  
      */
 109  
     protected BaseXPath(String xpathExpr) throws JaxenException
 110  6146
     {
 111  
         try
 112  
         {
 113  6146
             XPathReader reader = XPathReaderFactory.createReader();
 114  6144
             JaxenHandler handler = new JaxenHandler();
 115  6144
             reader.setXPathHandler( handler );
 116  6144
             reader.parse( xpathExpr );
 117  6064
             this.xpath = handler.getXPathExpr();
 118  
         }
 119  80
         catch (org.jaxen.saxpath.XPathSyntaxException e)
 120  
         {
 121  80
             throw new org.jaxen.XPathSyntaxException( e );
 122  
         }
 123  2
         catch (SAXPathException e)
 124  
         {
 125  2
             throw new JaxenException( e );
 126  6064
         }
 127  
 
 128  6064
         this.exprText = xpathExpr;
 129  6064
     }
 130  
 
 131  
     /** Construct given an XPath expression string.
 132  
      *
 133  
      *  @param xpathExpr the XPath expression
 134  
      *
 135  
      *  @param navigator the XML navigator to use
 136  
      *
 137  
      *  @throws JaxenException if there is a syntax error while
 138  
      *          parsing the expression
 139  
      */
 140  
     public BaseXPath(String xpathExpr, Navigator navigator) throws JaxenException
 141  
     {
 142  6146
         this( xpathExpr );
 143  6064
         this.navigator = navigator;
 144  6064
     }
 145  
 
 146  
     /** Evaluate this XPath against a given context.
 147  
      *  The context of evaluation may be any object type
 148  
      *  the navigator recognizes as a node.
 149  
      *  The return value is either a <code>String</code>,
 150  
      *  <code>Double</code>, <code>Boolean</code>, or <code>List</code>
 151  
      *  of nodes.
 152  
      *
 153  
      *  <p>
 154  
      *  When using this method, one must be careful to
 155  
      *  test the class of the returned object.  If the returned 
 156  
      *  object is a list, then the items in this 
 157  
      *  list will be the actual <code>Document</code>,
 158  
      *  <code>Element</code>, <code>Attribute</code>, etc. objects
 159  
      *  as defined by the concrete XML object-model implementation,
 160  
      *  directly from the context document.  This method <strong>does
 161  
      *  not return <em>copies</em> of anything</strong>, but merely 
 162  
      *  returns references to objects within the source document.
 163  
      *  </p>
 164  
      *  
 165  
      * @param context the node, node-set or Context object for evaluation. 
 166  
      *      This value can be null.
 167  
      *
 168  
      * @return the result of evaluating the XPath expression
 169  
      *          against the supplied context
 170  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 171  
      * @throws ClassCastException if the context is not a node
 172  
      */
 173  
     public Object evaluate(Object context) throws JaxenException
 174  
     {
 175  1712
         List answer = selectNodes(context);
 176  
 
 177  1692
         if ( answer != null
 178  
              &&
 179  1692
              answer.size() == 1 )
 180  
         {
 181  1680
             Object first = answer.get(0);
 182  
 
 183  1680
             if ( first instanceof String
 184  
                  ||
 185  
                  first instanceof Number
 186  
                  ||
 187  
                  first instanceof Boolean ) 
 188  
             {
 189  1412
                 return first;
 190  
             }
 191  
         }
 192  280
         return answer;
 193  
     }
 194  
     
 195  
     /** 
 196  
      *  List all the nodes selected by this XPath
 197  
      *  expression. If multiple nodes match, multiple nodes
 198  
      *  are returned. Nodes are returned
 199  
      *  in document-order, as defined by the XPath
 200  
      *  specification. If the expression selects a non-node-set
 201  
      *  (i.e. a number, boolean, or string) then a List
 202  
      *  containing just that one object is returned.
 203  
      *
 204  
      * @param node the node, node-set or Context object for evaluation. 
 205  
      *     This value can be null.
 206  
      *
 207  
      * @return the node-set of all items selected by this XPath expression
 208  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 209  
      *
 210  
      * @see #selectNodesForContext
 211  
      */
 212  
     public List selectNodes(Object node) throws JaxenException
 213  
     {
 214  3650
         Context context = getContext( node );
 215  3650
         return selectNodesForContext( context );
 216  
     }
 217  
 
 218  
     /** 
 219  
      * Return the first node selected by this XPath
 220  
      * expression. If multiple nodes match, only one node is
 221  
      * returned. The selected node will be the first
 222  
      * selected node in document-order, as defined by the XPath
 223  
      * specification.
 224  
      *
 225  
      * @param node the node, node-set or Context object for evaluation. 
 226  
      *     This value can be null.
 227  
      *
 228  
      * @return the node-set of all items selected
 229  
      *          by this XPath expression
 230  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 231  
      *
 232  
      * @see #selectNodes
 233  
      */
 234  
     public Object selectSingleNode(Object node) throws JaxenException
 235  
     {
 236  8
         List results = selectNodes( node );
 237  
 
 238  8
         if ( results.isEmpty() )
 239  
         {
 240  4
             return null;
 241  
         }
 242  
 
 243  4
         return results.get( 0 );
 244  
     }
 245  
 
 246  
     /**
 247  
      * Returns the XPath string-value of the argument node.
 248  
      * 
 249  
      * @param node the node whose value to take
 250  
      * @return the XPath string value of this node
 251  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 252  
      * @deprecated replaced by {@link #stringValueOf}
 253  
      */
 254  
     public String valueOf(Object node) throws JaxenException
 255  
     {
 256  0
         return stringValueOf( node );
 257  
     }
 258  
 
 259  
     /** Retrieves the string-value of the result of
 260  
      *  evaluating this XPath expression when evaluated 
 261  
      *  against the specified context.
 262  
      *
 263  
      *  <p>
 264  
      *  The string-value of the expression is determined per
 265  
      *  the <code>string(..)</code> core function defined
 266  
      *  in the XPath specification.  This means that an expression
 267  
      *  that selects zero nodes will return the empty string,
 268  
      *  while an expression that selects one-or-more nodes will
 269  
      *  return the string-value of the first node.
 270  
      *  </p>
 271  
      *
 272  
      * @param node the node, node-set or Context object for evaluation. This value can be null.
 273  
      *
 274  
      * @return the string-value of the result of evaluating this expression with the specified context node
 275  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 276  
      */
 277  
     public String stringValueOf(Object node) throws JaxenException
 278  
     {
 279  6
         Context context = getContext( node );
 280  
         
 281  6
         Object result = selectSingleNodeForContext( context );
 282  
 
 283  6
         if ( result == null )
 284  
         {
 285  2
             return "";
 286  
         }
 287  
 
 288  8
         return StringFunction.evaluate( result,
 289  4
                                         context.getNavigator() );
 290  
     }
 291  
 
 292  
     /** Retrieve a boolean-value interpretation of this XPath
 293  
      *  expression when evaluated against a given context.
 294  
      *
 295  
      *  <p>
 296  
      *  The boolean-value of the expression is determined per
 297  
      *  the <code>boolean(..)</code> function defined
 298  
      *  in the XPath specification.  This means that an expression
 299  
      *  that selects zero nodes will return <code>false</code>,
 300  
      *  while an expression that selects one or more nodes will
 301  
      *  return <code>true</code>.
 302  
      *  </p>
 303  
      *
 304  
      * @param node the node, node-set or Context object for evaluation. This value can be null.
 305  
      *
 306  
      * @return the boolean-value of the result of evaluating this expression with the specified context node
 307  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 308  
      */
 309  
     public boolean booleanValueOf(Object node) throws JaxenException
 310  
     {
 311  8
         Context context = getContext( node );
 312  8
         List result = selectNodesForContext( context );
 313  8
         if ( result == null ) return false;
 314  8
         return BooleanFunction.evaluate( result, context.getNavigator() ).booleanValue();
 315  
     }
 316  
 
 317  
     /** Retrieve a number-value interpretation of this XPath
 318  
      *  expression when evaluated against a given context.
 319  
      *
 320  
      *  <p>
 321  
      *  The number-value of the expression is determined per
 322  
      *  the <code>number(..)</code> core function as defined
 323  
      *  in the XPath specification. This means that if this
 324  
      *  expression selects multiple nodes, the number-value
 325  
      *  of the first node is returned.
 326  
      *  </p>
 327  
      *
 328  
      * @param node the node, node-set or Context object for evaluation. This value can be null.
 329  
      *
 330  
      * @return a <code>Double</code> indicating the numeric value of
 331  
      *      evaluating this expression against the specified context
 332  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 333  
      */
 334  
     public Number numberValueOf(Object node) throws JaxenException
 335  
     {
 336  6
         Context context = getContext( node );
 337  6
         Object result = selectSingleNodeForContext( context );
 338  12
         return NumberFunction.evaluate( result,
 339  6
                                         context.getNavigator() );
 340  
     }
 341  
 
 342  
     // Helpers
 343  
 
 344  
     /** Add a namespace prefix-to-URI mapping for this XPath
 345  
      *  expression.
 346  
      *
 347  
      *  <p>
 348  
      *  Namespace prefix-to-URI mappings in an XPath are independent
 349  
      *  of those used within any document.  Only the mapping explicitly
 350  
      *  added to this XPath will be available for resolving the
 351  
      *  XPath expression.
 352  
      *  </p>
 353  
      *
 354  
      *  <p>
 355  
      *  This is a convenience method for adding mappings to the
 356  
      *  default {@link NamespaceContext} in place for this XPath.
 357  
      *  If you have installed a custom <code>NamespaceContext</code>
 358  
      *  that is not a <code>SimpleNamespaceContext</code>,
 359  
      *  then this method will throw a <code>JaxenException</code>.
 360  
      *  </p>
 361  
      *
 362  
      *  @param prefix the namespace prefix
 363  
      *  @param uri the namespace URI
 364  
      *
 365  
      *  @throws JaxenException if the <code>NamespaceContext</code>
 366  
      *          used by this XPath is not a <code>SimpleNamespaceContext</code>
 367  
      */
 368  
     public void addNamespace(String prefix,
 369  
                              String uri) throws JaxenException
 370  
     {
 371  16
         NamespaceContext nsContext = getNamespaceContext();
 372  16
         if ( nsContext instanceof SimpleNamespaceContext )
 373  
         {
 374  14
             ((SimpleNamespaceContext)nsContext).addNamespace( prefix,
 375  
                                                               uri );
 376  14
             return;
 377  
         }
 378  
 
 379  2
         throw new JaxenException("Operation not permitted while using a non-simple namespace context.");
 380  
     }
 381  
 
 382  
 
 383  
     // ------------------------------------------------------------
 384  
     // ------------------------------------------------------------
 385  
     //     Properties
 386  
     // ------------------------------------------------------------
 387  
     // ------------------------------------------------------------
 388  
 
 389  
     
 390  
     /** Set a <code>NamespaceContext</code> for use with this
 391  
      *  XPath expression.
 392  
      *
 393  
      *  <p>
 394  
      *  A <code>NamespaceContext</code> is responsible for translating
 395  
      *  namespace prefixes within the expression into namespace URIs.
 396  
      *  </p>
 397  
      *
 398  
      *  @param namespaceContext the <code>NamespaceContext</code> to
 399  
      *         install for this expression
 400  
      *
 401  
      *  @see NamespaceContext
 402  
      *  @see NamespaceContext#translateNamespacePrefixToUri
 403  
      */
 404  
     public void setNamespaceContext(NamespaceContext namespaceContext)
 405  
     {
 406  66
         getContextSupport().setNamespaceContext(namespaceContext);
 407  66
     }
 408  
 
 409  
     /** Set a <code>FunctionContext</code> for use with this XPath
 410  
      *  expression.
 411  
      *
 412  
      *  <p>
 413  
      *  A <code>FunctionContext</code> is responsible for resolving
 414  
      *  all function calls used within the expression.
 415  
      *  </p>
 416  
      *
 417  
      *  @param functionContext the <code>FunctionContext</code> to
 418  
      *         install for this expression
 419  
      *
 420  
      *  @see FunctionContext
 421  
      *  @see FunctionContext#getFunction
 422  
      */
 423  
     public void setFunctionContext(FunctionContext functionContext)
 424  
     {
 425  58
         getContextSupport().setFunctionContext(functionContext);
 426  58
     }
 427  
 
 428  
     /** Set a <code>VariableContext</code> for use with this XPath
 429  
      *  expression.
 430  
      *
 431  
      *  <p>
 432  
      *  A <code>VariableContext</code> is responsible for resolving
 433  
      *  all variables referenced within the expression.
 434  
      *  </p>
 435  
      *
 436  
      *  @param variableContext The <code>VariableContext</code> to
 437  
      *         install for this expression
 438  
      *
 439  
      *  @see VariableContext
 440  
      *  @see VariableContext#getVariableValue
 441  
      */
 442  
     public void setVariableContext(VariableContext variableContext)
 443  
     {
 444  58
         getContextSupport().setVariableContext(variableContext);
 445  58
     }
 446  
 
 447  
     /** Retrieve the <code>NamespaceContext</code> used by this XPath
 448  
      *  expression.
 449  
      *
 450  
      *  <p>
 451  
      *  A <code>NamespaceContext</code> is responsible for mapping
 452  
      *  prefixes used within the expression to namespace URIs.
 453  
      *  </p>
 454  
      *
 455  
      *  <p>
 456  
      *  If this XPath expression has not previously had a <code>NamespaceContext</code>
 457  
      *  installed, a new default <code>NamespaceContext</code> will be created,
 458  
      *  installed and returned.
 459  
      *  </p>
 460  
      *
 461  
      *  @return the <code>NamespaceContext</code> used by this expression
 462  
      *
 463  
      *  @see NamespaceContext
 464  
      */
 465  
     public NamespaceContext getNamespaceContext()
 466  
     {
 467  18
         return getContextSupport().getNamespaceContext();
 468  
     }
 469  
 
 470  
     /** Retrieve the <code>FunctionContext</code> used by this XPath
 471  
      *  expression.
 472  
      *
 473  
      *  <p>
 474  
      *  A <code>FunctionContext</code> is responsible for resolving
 475  
      *  all function calls used within the expression.
 476  
      *  </p>
 477  
      *
 478  
      *  <p>
 479  
      *  If this XPath expression has not previously had a <code>FunctionContext</code>
 480  
      *  installed, a new default <code>FunctionContext</code> will be created,
 481  
      *  installed and returned.
 482  
      *  </p>
 483  
      *
 484  
      *  @return the <code>FunctionContext</code> used by this expression
 485  
      *
 486  
      *  @see FunctionContext
 487  
      */
 488  
     public FunctionContext getFunctionContext()
 489  
     {
 490  4
         return getContextSupport().getFunctionContext();
 491  
     }
 492  
 
 493  
     /** Retrieve the <code>VariableContext</code> used by this XPath
 494  
      *  expression.
 495  
      *
 496  
      *  <p>
 497  
      *  A <code>VariableContext</code> is responsible for resolving
 498  
      *  all variables referenced within the expression.
 499  
      *  </p>
 500  
      *
 501  
      *  <p>
 502  
      *  If this XPath expression has not previously had a <code>VariableContext</code>
 503  
      *  installed, a new default <code>VariableContext</code> will be created,
 504  
      *  installed and returned.
 505  
      *  </p>
 506  
      *  
 507  
      *  @return the <code>VariableContext</code> used by this expression
 508  
      *
 509  
      *  @see VariableContext
 510  
      */
 511  
     public VariableContext getVariableContext()
 512  
     {
 513  2
         return getContextSupport().getVariableContext();
 514  
     }
 515  
     
 516  
     
 517  
     /** Retrieve the root expression of the internal
 518  
      *  compiled form of this XPath expression.
 519  
      *
 520  
      *  <p>
 521  
      *  Internally, Jaxen maintains a form of Abstract Syntax
 522  
      *  Tree (AST) to represent the structure of the XPath expression.
 523  
      *  This is normally not required during normal consumer-grade
 524  
      *  usage of Jaxen.  This method is provided for hard-core users
 525  
      *  who wish to manipulate or inspect a tree-based version of
 526  
      *  the expression.
 527  
      *  </p>
 528  
      *
 529  
      *  @return the root of the AST of this expression
 530  
      */
 531  
     public Expr getRootExpr() 
 532  
     {
 533  7054
         return xpath.getRootExpr();
 534  
     }
 535  
     
 536  
     /** Return the original expression text.
 537  
      *
 538  
      *  @return the normalized XPath expression string
 539  
      */
 540  
     public String toString()
 541  
     {
 542  516
         return this.exprText;
 543  
     }
 544  
 
 545  
     /** Returns a string representation of the parse tree.
 546  
      *
 547  
      *  @return a string representation of the parse tree.
 548  
      */
 549  
     public String debug()
 550  
     {
 551  2
         return this.xpath.toString();
 552  
     }
 553  
     
 554  
     // ------------------------------------------------------------
 555  
     // ------------------------------------------------------------
 556  
     //     Implementation methods
 557  
     // ------------------------------------------------------------
 558  
     // ------------------------------------------------------------
 559  
 
 560  
     
 561  
     /** Create a {@link Context} wrapper for the provided
 562  
      *  implementation-specific object.
 563  
      *
 564  
      *  @param node the implementation-specific object 
 565  
      *         to be used as the context
 566  
      *
 567  
      *  @return a <code>Context</code> wrapper around the object
 568  
      */
 569  
     protected Context getContext(Object node)
 570  
     {
 571  3670
         if ( node instanceof Context )
 572  
         {
 573  2448
             return (Context) node;
 574  
         }
 575  
 
 576  1222
         Context fullContext = new Context( getContextSupport() );
 577  
 
 578  1222
         if ( node instanceof List )
 579  
         {
 580  4
             fullContext.setNodeSet( (List) node );
 581  
         }
 582  
         else
 583  
         {
 584  1218
             List list = new SingletonList(node);
 585  1218
             fullContext.setNodeSet( list );
 586  
         }
 587  
 
 588  1222
         return fullContext;
 589  
     }
 590  
 
 591  
     /** Retrieve the {@link ContextSupport} aggregation of
 592  
      *  <code>NamespaceContext</code>, <code>FunctionContext</code>,
 593  
      *  <code>VariableContext</code>, and {@link Navigator}.
 594  
      *
 595  
      *  @return aggregate <code>ContextSupport</code> for this
 596  
      *          XPath expression
 597  
      */
 598  
     protected ContextSupport getContextSupport()
 599  
     {
 600  1428
         if ( support == null )
 601  
         {
 602  1284
             support = new ContextSupport( 
 603  1284
                 createNamespaceContext(),
 604  1284
                 createFunctionContext(),
 605  1284
                 createVariableContext(),
 606  1284
                 getNavigator() 
 607  
             );
 608  
         }
 609  
 
 610  1428
         return support;
 611  
     }
 612  
 
 613  
     /** Retrieve the XML object-model-specific {@link Navigator} 
 614  
      *  for us in evaluating this XPath expression.
 615  
      *
 616  
      *  @return the implementation-specific <code>Navigator</code>
 617  
      */
 618  
     public Navigator getNavigator()
 619  
     {
 620  1286
         return navigator;
 621  
     }
 622  
     
 623  
     
 624  
 
 625  
     // ------------------------------------------------------------
 626  
     // ------------------------------------------------------------
 627  
     //     Factory methods for default contexts
 628  
     // ------------------------------------------------------------
 629  
     // ------------------------------------------------------------
 630  
 
 631  
     /** Create a default <code>FunctionContext</code>.
 632  
      *
 633  
      *  @return a default <code>FunctionContext</code>
 634  
      */
 635  
     protected FunctionContext createFunctionContext()
 636  
     {
 637  1284
         return XPathFunctionContext.getInstance();
 638  
     }
 639  
     
 640  
     /** Create a default <code>NamespaceContext</code>.
 641  
      *
 642  
      *  @return a default <code>NamespaceContext</code> instance
 643  
      */
 644  
     protected NamespaceContext createNamespaceContext()
 645  
     {
 646  1284
         return new SimpleNamespaceContext();
 647  
     }
 648  
     
 649  
     /** Create a default <code>VariableContext</code>.
 650  
      *
 651  
      *  @return a default <code>VariableContext</code> instance
 652  
      */
 653  
     protected VariableContext createVariableContext()
 654  
     {
 655  1284
         return new SimpleVariableContext();
 656  
     }
 657  
     
 658  
     /** Select all nodes that match this XPath
 659  
      *  expression on the given Context object. 
 660  
      *  If multiple nodes match, multiple nodes
 661  
      *  will be returned in document-order, as defined by the XPath
 662  
      *  specification. If the expression selects a non-node-set
 663  
      *  (i.e. a number, boolean, or string) then a List
 664  
      *  containing just that one object is returned.
 665  
      *
 666  
      * @param context the Context which gets evaluated
 667  
      *
 668  
      * @return the node-set of all items selected
 669  
      *          by this XPath expression
 670  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 671  
      *
 672  
      */
 673  
     protected List selectNodesForContext(Context context) throws JaxenException
 674  
     {
 675  3670
         List list = this.xpath.asList( context );
 676  3546
         return list;
 677  
         
 678  
     }
 679  
  
 680  
 
 681  
     /** Return only the first node that is selected by this XPath
 682  
      *  expression.  If multiple nodes match, only one node will be
 683  
      *  returned. The selected node will be the first
 684  
      *  selected node in document-order, as defined by the XPath
 685  
      *  specification. If the XPath expression selects a double,
 686  
      *  String, or boolean, then that object is returned.
 687  
      *
 688  
      * @param context the Context against which this expression is evaluated
 689  
      *
 690  
      * @return the first node in document order of all nodes selected
 691  
      *          by this XPath expression
 692  
      * @throws JaxenException if an XPath error occurs during expression evaluation
 693  
      *
 694  
      * @see #selectNodesForContext
 695  
      */
 696  
     protected Object selectSingleNodeForContext(Context context) throws JaxenException
 697  
     {
 698  12
         List results = selectNodesForContext(context);
 699  
 
 700  12
         if ( results.isEmpty() )
 701  
         {
 702  4
             return null;
 703  
         }
 704  
 
 705  8
         return results.get( 0 );
 706  
     }
 707  
     
 708  
 }