KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jaxen > BaseXPath


1 /*
2  * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org/jaxen/BaseXPath.java,v 1.43 2005/06/20 11:18:51 elharo Exp $
3  * $Revision: 1.43 $
4  * $Date: 2005/06/20 11:18:51 $
5  *
6  * ====================================================================
7  *
8  * Copyright (C) 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
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions, and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions, and the disclaimer that follows
20  * these conditions in the documentation and/or other materials
21  * provided with the distribution.
22  *
23  * 3. The name "Jaxen" must not be used to endorse or promote products
24  * derived from this software without prior written permission. For
25  * written permission, please contact license@jaxen.org.
26  *
27  * 4. Products derived from this software may not be called "Jaxen", nor
28  * may "Jaxen" appear in their name, without prior written permission
29  * from the Jaxen Project Management (pm@jaxen.org).
30  *
31  * In addition, we request (but do not require) that you include in the
32  * end-user documentation provided with the redistribution and/or in the
33  * software itself an acknowledgement equivalent to the following:
34  * "This product includes software developed by the
35  * Jaxen Project (http://www.jaxen.org/)."
36  * Alternatively, the acknowledgment may be graphical using the logos
37  * available at http://www.jaxen.org/
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
43  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  *
52  * ====================================================================
53  * This software consists of voluntary contributions made by many
54  * individuals on behalf of the Jaxen Project and was originally
55  * created by bob mcwhirter <bob@werken.com> and
56  * James Strachan <jstrachan@apache.org>. For more information on the
57  * Jaxen Project, please see <http://www.jaxen.org/>.
58  *
59  * $Id: BaseXPath.java,v 1.43 2005/06/20 11:18:51 elharo Exp $
60  */

61
62
63 package org.jaxen;
64
65 import java.io.Serializable JavaDoc;
66 import java.util.List JavaDoc;
67
68 import org.jaxen.expr.Expr;
69 import org.jaxen.expr.XPathExpr;
70 import org.jaxen.function.BooleanFunction;
71 import org.jaxen.function.NumberFunction;
72 import org.jaxen.function.StringFunction;
73 import org.jaxen.saxpath.SAXPathException;
74 import org.jaxen.saxpath.XPathReader;
75 import org.jaxen.saxpath.helpers.XPathReaderFactory;
76 import org.jaxen.util.SingletonList;
77
78 /** Base functionality for all concrete, implementation-specific XPaths.
79  *
80  * <p>
81  * This class provides generic functionality for further-defined
82  * implementation-specific XPaths.
83  * </p>
84  *
85  * <p>
86  * If you want to adapt the Jaxen engine so that it can traverse your own
87  * object model, then this is a good base class to derive from.
88  * Typically you only really need to provide your own
89  * {@link org.jaxen.Navigator} implementation.
90  * </p>
91  *
92  * @see org.jaxen.dom4j.Dom4jXPath XPath for dom4j
93  * @see org.jaxen.jdom.JDOMXPath XPath for JDOM
94  * @see org.jaxen.dom.DOMXPath XPath for W3C DOM
95  *
96  * @author <a HREF="mailto:bob@werken.com">bob mcwhirter</a>
97  * @author <a HREF="mailto:jstrachan@apache.org">James Strachan</a>
98  */

99 public class BaseXPath implements XPath, Serializable JavaDoc
100 {
101     /** Original expression text. */
102     private String JavaDoc exprText;
103
104     /** the parsed form of the XPath expression */
105     private XPathExpr xpath;
106     
107     /** the support information and function, namespace and variable contexts */
108     private ContextSupport support;
109
110     /** the implementation-specific Navigator for retrieving XML nodes **/
111     private Navigator navigator;
112     
113     /** Construct given an XPath expression string.
114      *
115      * @param xpathExpr the XPath expression
116      *
117      * @throws JaxenException if there is a syntax error while
118      * parsing the expression
119      */

120     protected BaseXPath(String JavaDoc xpathExpr) throws JaxenException
121     {
122         try
123         {
124             XPathReader reader = XPathReaderFactory.createReader();
125             JaxenHandler handler = new JaxenHandler();
126             reader.setXPathHandler( handler );
127             reader.parse( xpathExpr );
128             this.xpath = handler.getXPathExpr();
129         }
130         catch (org.jaxen.saxpath.XPathSyntaxException e)
131         {
132             throw new org.jaxen.XPathSyntaxException( e );
133         }
134         catch (SAXPathException e)
135         {
136             throw new JaxenException( e );
137         }
138
139         this.exprText = xpathExpr;
140     }
141
142     /** Construct given an XPath expression string.
143      *
144      * @param xpathExpr the XPath expression
145      *
146      * @param navigator the XML navigator to use
147      *
148      * @throws JaxenException if there is a syntax error while
149      * parsing the expression
150      */

151     public BaseXPath(String JavaDoc xpathExpr, Navigator navigator) throws JaxenException
152     {
153         this( xpathExpr );
154         this.navigator = navigator;
155     }
156
157     /** Evaluate this XPath against a given context.
158      * The context of evaluation may be any object type
159      * the navigator recognizes as a node.
160      * The return value is either a <code>String</code>,
161      * <code>Double</code>, <code>Boolean</code>, or <code>List</code>
162      * of nodes.
163      *
164      * <p>
165      * When using this method, one must be careful to
166      * test the class of the returned object. If the returned
167      * object is a list, then the items in this
168      * list will be the actual <code>Document</code>,
169      * <code>Element</code>, <code>Attribute</code>, etc. objects
170      * as defined by the concrete XML object-model implementation,
171      * directly from the context document. This method <strong>does
172      * not return <em>copies</em> of anything</strong>, but merely
173      * returns references to objects within the source document.
174      * </p>
175      *
176      * @param context the node, node-set or Context object for evaluation.
177      * This value can be null.
178      *
179      * @return the result of evaluating the XPath expression
180      * against the supplied context
181      * @throws JaxenException if an XPath error occurs during expression evaluation
182      * @throws ClassCastException if the context is not a node
183      */

184     public Object JavaDoc evaluate(Object JavaDoc context) throws JaxenException
185     {
186         List JavaDoc answer = selectNodes(context);
187
188         if ( answer != null
189              &&
190              answer.size() == 1 )
191         {
192             Object JavaDoc first = answer.get(0);
193
194             if ( first instanceof String JavaDoc
195                  ||
196                  first instanceof Number JavaDoc
197                  ||
198                  first instanceof Boolean JavaDoc )
199             {
200                 return first;
201             }
202         }
203         return answer;
204     }
205     
206     /** Select all nodes that are selected by this XPath
207      * expression. If multiple nodes match, multiple nodes
208      * will be returned. Nodes will be returned
209      * in document-order, as defined by the XPath
210      * specification. If the expression selects a non-node-set
211      * (i.e. a number, boolean, or string) then a List
212      * containing just that one object is returned.
213      * </p>
214      *
215      * @param node the node, node-set or Context object for evaluation.
216      * This value can be null.
217      *
218      * @return the node-set of all items selected
219      * by this XPath expression
220      * @throws JaxenException if an XPath error occurs during expression evaluation
221      *
222      * @see #selectNodesForContext
223      */

224     public List JavaDoc selectNodes(Object JavaDoc node) throws JaxenException
225     {
226         Context context = getContext( node );
227         return selectNodesForContext( context );
228     }
229
230     /** Select only the first node selected by this XPath
231      * expression. If multiple nodes match, only one node will be
232      * returned. The selected node will be the first
233      * selected node in document-order, as defined by the XPath
234      * specification.
235      * </p>
236      *
237      * @param node the node, node-set or Context object for evaluation.
238      * This value can be null.
239      *
240      * @return the node-set of all items selected
241      * by this XPath expression
242      * @throws JaxenException if an XPath error occurs during expression evaluation
243      *
244      * @see #selectNodes
245      */

246     public Object JavaDoc selectSingleNode(Object JavaDoc node) throws JaxenException
247     {
248         List JavaDoc results = selectNodes( node );
249
250         if ( results.isEmpty() )
251         {
252             return null;
253         }
254
255         return results.get( 0 );
256     }
257
258     /**
259      * Returns the XPath string-value of the argument node.
260      *
261      * @param node the node whose value to take
262      * @return the XPath string value of this node
263      * @throws JaxenException if an XPath error occurs during expression evaluation
264      * @deprecated
265      * @see #stringValueOf
266      */

267     public String JavaDoc valueOf(Object JavaDoc node) throws JaxenException
268     {
269         return stringValueOf( node );
270     }
271
272     /** Retrieves the string-value of the result of
273      * evaluating this XPath expression when evaluated
274      * against the specified context.
275      *
276      * <p>
277      * The string-value of the expression is determined per
278      * the <code>string(..)</code> core function defined
279      * in the XPath specification. This means that an expression
280      * that selects zero nodes will return the empty string,
281      * while an expression that selects one-or-more nodes will
282      * return the string-value of the first node.
283      * </p>
284      *
285      * @param node the node, node-set or Context object for evaluation. This value can be null.
286      *
287      * @return the string-value of the result of evaluating this expression with the specified context node
288      * @throws JaxenException if an XPath error occurs during expression evaluation
289      */

290     public String JavaDoc stringValueOf(Object JavaDoc node) throws JaxenException
291     {
292         Context context = getContext( node );
293         
294         Object JavaDoc result = selectSingleNodeForContext( context );
295
296         if ( result == null )
297         {
298             return "";
299         }
300
301         return StringFunction.evaluate( result,
302                                         context.getNavigator() );
303     }
304
305     /** Retrieve a boolean-value interpretation of this XPath
306      * expression when evaluated against a given context.
307      *
308      * <p>
309      * The boolean-value of the expression is determined per
310      * the <code>boolean(..)</code> function defined
311      * in the XPath specification. This means that an expression
312      * that selects zero nodes will return <code>false</code>,
313      * while an expression that selects one or more nodes will
314      * return <code>true</code>.
315      * </p>
316      *
317      * @param node the node, node-set or Context object for evaluation. This value can be null.
318      *
319      * @return the boolean-value of the result of evaluating this expression with the specified context node
320      * @throws JaxenException if an XPath error occurs during expression evaluation
321      */

322     public boolean booleanValueOf(Object JavaDoc node) throws JaxenException
323     {
324         Context context = getContext( node );
325         List JavaDoc result = selectNodesForContext( context );
326         if ( result == null ) return false;
327         return BooleanFunction.evaluate( result, context.getNavigator() ).booleanValue();
328     }
329
330     /** Retrieve a number-value interpretation of this XPath
331      * expression when evaluated against a given context.
332      *
333      * <p>
334      * The number-value of the expression is determined per
335      * the <code>number(..)</code> core function as defined
336      * in the XPath specification. This means that if this
337      * expression selects multiple nodes, the number-value
338      * of the first node is returned.
339      * </p>
340      *
341      * @param node the node, node-set or Context object for evaluation. This value can be null.
342      *
343      * @return a <code>Double</code> indicating the numeric value of
344      * evaluating this expression against the specified context
345      * @throws JaxenException if an XPath error occurs during expression evaluation
346      */

347     public Number JavaDoc numberValueOf(Object JavaDoc node) throws JaxenException
348     {
349         Context context = getContext( node );
350         Object JavaDoc result = selectSingleNodeForContext( context );
351         return NumberFunction.evaluate( result,
352                                         context.getNavigator() );
353     }
354
355     // Helpers
356

357     /** Add a namespace prefix-to-URI mapping for this XPath
358      * expression.
359      *
360      * <p>
361      * Namespace prefix-to-URI mappings in an XPath are independent
362      * of those used within any document. Only the mapping explicitly
363      * added to this XPath will be available for resolving the
364      * XPath expression.
365      * </p>
366      *
367      * <p>
368      * This is a convenience method for adding mappings to the
369      * default {@link NamespaceContext} in place for this XPath.
370      * If you have installed a specific custom <code>NamespaceContext</code>,
371      * then this method will throw a <code>JaxenException</code>.
372      * </p>
373      *
374      * @param prefix the namespace prefix
375      * @param uri the namespace URI
376      *
377      * @throws JaxenException if a <code>NamespaceContext</code>
378      * used by this XPath has been explicitly installed
379      */

380     public void addNamespace(String JavaDoc prefix,
381                              String JavaDoc uri) throws JaxenException
382     {
383         NamespaceContext nsContext = getNamespaceContext();
384         if ( nsContext instanceof SimpleNamespaceContext )
385         {
386             ((SimpleNamespaceContext)nsContext).addNamespace( prefix,
387                                                               uri );
388             return;
389         }
390
391         throw new JaxenException("Operation not permitted while using a custom namespace context.");
392     }
393
394
395     // ------------------------------------------------------------
396
// ------------------------------------------------------------
397
// Properties
398
// ------------------------------------------------------------
399
// ------------------------------------------------------------
400

401     
402     /** Set a <code>NamespaceContext</code> for use with this
403      * XPath expression.
404      *
405      * <p>
406      * A <code>NamespaceContext</code> is responsible for translating
407      * namespace prefixes within the expression into namespace URIs.
408      * </p>
409      *
410      * @param namespaceContext the <code>NamespaceContext</code> to
411      * install for this expression
412      *
413      * @see NamespaceContext
414      * @see NamespaceContext#translateNamespacePrefixToUri
415      */

416     public void setNamespaceContext(NamespaceContext namespaceContext)
417     {
418         getContextSupport().setNamespaceContext(namespaceContext);
419     }
420
421     /** Set a <code>FunctionContext</code> for use with this XPath
422      * expression.
423      *
424      * <p>
425      * A <code>FunctionContext</code> is responsible for resolving
426      * all function calls used within the expression.
427      * </p>
428      *
429      * @param functionContext the <code>FunctionContext</code> to
430      * install for this expression
431      *
432      * @see FunctionContext
433      * @see FunctionContext#getFunction
434      */

435     public void setFunctionContext(FunctionContext functionContext)
436     {
437         getContextSupport().setFunctionContext(functionContext);
438     }
439
440     /** Set a <code>VariableContext</code> for use with this XPath
441      * expression.
442      *
443      * <p>
444      * A <code>VariableContext</code> is responsible for resolving
445      * all variables referenced within the expression.
446      * </p>
447      *
448      * @param variableContext The <code>VariableContext</code> to
449      * install for this expression
450      *
451      * @see VariableContext
452      * @see VariableContext#getVariableValue
453      */

454     public void setVariableContext(VariableContext variableContext)
455     {
456         getContextSupport().setVariableContext(variableContext);
457     }
458
459     /** Retrieve the <code>NamespaceContext</code> used by this XPath
460      * expression.
461      *
462      * <p>
463      * A <code>NamespaceContext</code> is responsible for mapping
464      * prefixes used within the expression to namespace URIs.
465      * </p>
466      *
467      * <p>
468      * If this XPath expression has not previously had a <code>NamespaceContext</code>
469      * installed, a new default <code>NamespaceContext</code> will be created,
470      * installed and returned.
471      * </p>
472      *
473      * @return the <code>NamespaceContext</code> used by this expression
474      *
475      * @see NamespaceContext
476      */

477     public NamespaceContext getNamespaceContext()
478     {
479         NamespaceContext answer = getContextSupport().getNamespaceContext();
480         if ( answer == null ) {
481             answer = createNamespaceContext();
482             getContextSupport().setNamespaceContext( answer );
483         }
484         return answer;
485     }
486
487     /** Retrieve the <code>FunctionContext</code> used by this XPath
488      * expression.
489      *
490      * <p>
491      * A <code>FunctionContext</code> is responsible for resolving
492      * all function calls used within the expression.
493      * </p>
494      *
495      * <p>
496      * If this XPath expression has not previously had a <code>FunctionContext</code>
497      * installed, a new default <code>FunctionContext</code> will be created,
498      * installed and returned.
499      * </p>
500      *
501      * @return the <code>FunctionContext</code> used by this expression
502      *
503      * @see FunctionContext
504      */

505     public FunctionContext getFunctionContext()
506     {
507         FunctionContext answer = getContextSupport().getFunctionContext();
508         if ( answer == null ) {
509             answer = createFunctionContext();
510             getContextSupport().setFunctionContext( answer );
511         }
512         return answer;
513     }
514
515     /** Retrieve the <code>VariableContext</code> used by this XPath
516      * expression.
517      *
518      * <p>
519      * A <code>VariableContext</code> is responsible for resolving
520      * all variables referenced within the expression.
521      * </p>
522      *
523      * <p>
524      * If this XPath expression has not previously had a <code>VariableContext</code>
525      * installed, a new default <code>VariableContext</code> will be created,
526      * installed and returned.
527      * </p>
528      *
529      * @return the <code>VariableContext</code> used by this expression
530      *
531      * @see VariableContext
532      */

533     public VariableContext getVariableContext()
534     {
535         VariableContext answer = getContextSupport().getVariableContext();
536         if ( answer == null ) {
537             answer = createVariableContext();
538             getContextSupport().setVariableContext( answer );
539         }
540         return answer;
541     }
542     
543     
544     /** Retrieve the root expression of the internal
545      * compiled form of this XPath expression.
546      *
547      * <p>
548      * Internally, Jaxen maintains a form of Abstract Syntax
549      * Tree (AST) to represent the structure of the XPath expression.
550      * This is normally not required during normal consumer-grade
551      * usage of Jaxen. This method is provided for hard-core users
552      * who wish to manipulate or inspect a tree-based version of
553      * the expression.
554      * </p>
555      *
556      * @return the root of the AST of this expression
557      */

558     public Expr getRootExpr()
559     {
560         return xpath.getRootExpr();
561     }
562     
563     /** Return the original expression text.
564      *
565      * @return the normalized XPath expression string
566      */

567     public String JavaDoc toString()
568     {
569         return this.exprText;
570     }
571
572     /** Returns the string version of this xpath.
573      *
574      * @return the normalized XPath expression string
575      *
576      * @see #toString
577      */

578     public String JavaDoc debug()
579     {
580         return this.xpath.toString();
581     }
582     
583     // ------------------------------------------------------------
584
// ------------------------------------------------------------
585
// Implementation methods
586
// ------------------------------------------------------------
587
// ------------------------------------------------------------
588

589     
590     /** Create a {@link Context} wrapper for the provided
591      * implementation-specific object.
592      *
593      * @param node the implementation-specific object
594      * to be used as the context
595      *
596      * @return a <code>Context</code> wrapper around the object
597      */

598     protected Context getContext(Object JavaDoc node)
599     {
600         if ( node instanceof Context )
601         {
602             return (Context) node;
603         }
604
605         Context fullContext = new Context( getContextSupport() );
606
607         if ( node instanceof List JavaDoc )
608         {
609             fullContext.setNodeSet( (List JavaDoc) node );
610         }
611         else
612         {
613             List JavaDoc list = new SingletonList(node);
614             fullContext.setNodeSet( list );
615         }
616
617         return fullContext;
618     }
619
620     /** Retrieve the {@link ContextSupport} aggregation of
621      * <code>NamespaceContext</code>, <code>FunctionContext</code>,
622      * <code>VariableContext</code>, and {@link Navigator}.
623      *
624      * @return aggregate <code>ContextSupport</code> for this
625      * XPath expression
626      */

627     protected ContextSupport getContextSupport()
628     {
629         if ( support == null )
630         {
631             support = new ContextSupport(
632                 createNamespaceContext(),
633                 createFunctionContext(),
634                 createVariableContext(),
635                 getNavigator()
636             );
637         }
638
639         return support;
640     }
641
642     /** Retrieve the XML object-model-specific {@link Navigator}
643      * for us in evaluating this XPath expression.
644      *
645      * @return the implementation-specific <code>Navigator</code>
646      */

647     public Navigator getNavigator()
648     {
649         return navigator;
650     }
651     
652     
653
654     // ------------------------------------------------------------
655
// ------------------------------------------------------------
656
// Factory methods for default contexts
657
// ------------------------------------------------------------
658
// ------------------------------------------------------------
659

660     /** Create a default <code>FunctionContext</code>.
661      *
662      * @return a default <code>FunctionContext</code>
663      */

664     protected FunctionContext createFunctionContext()
665     {
666         return XPathFunctionContext.getInstance();
667     }
668     
669     /** Create a default <code>NamespaceContext</code>.
670      *
671      * @return a default <code>NamespaceContext</code> instance
672      */

673     protected NamespaceContext createNamespaceContext()
674     {
675         return new SimpleNamespaceContext();
676     }
677     
678     /** Create a default <code>VariableContext</code>.
679      *
680      * @return a default <code>VariableContext</code> instance
681      */

682     protected VariableContext createVariableContext()
683     {
684         return new SimpleVariableContext();
685     }
686     
687     /** Select all nodes that match this XPath
688      * expression on the given Context object.
689      * If multiple nodes match, multiple nodes
690      * will be returned in document-order, as defined by the XPath
691      * specification. If the expression selects a non-node-set
692      * (i.e. a number, boolean, or string) then a List
693      * containing just that one object is returned.
694      * </p>
695      *
696      * @param context the Context which gets evaluated
697      *
698      * @return the node-set of all items selected
699      * by this XPath expression
700      * @throws JaxenException if an XPath error occurs during expression evaluation
701      *
702      */

703     protected List JavaDoc selectNodesForContext(Context context) throws JaxenException
704     {
705         List JavaDoc list = this.xpath.asList( context );
706         return list;
707         
708     }
709  
710
711     /** Return only the first node that is selected by this XPath
712      * expression. If multiple nodes match, only one node will be
713      * returned. The selected node will be the first
714      * selected node in document-order, as defined by the XPath
715      * specification. If the XPath expression selects a double,
716      * String, or boolean, then that object is returned.
717      * </p>
718      *
719      * @param context the Context against which this expression is evaluated
720      *
721      * @return the first node in document order of all nodes selected
722      * by this XPath expression
723      * @throws JaxenException if an XPath error occurs during expression evaluation
724      *
725      * @see #selectNodesForContext
726      */

727     protected Object JavaDoc selectSingleNodeForContext(Context context) throws JaxenException
728     {
729         List JavaDoc results = selectNodesForContext( context );
730
731         if ( results.isEmpty() )
732         {
733             return null;
734         }
735
736         return results.get( 0 );
737     }
738     
739 }
740
Popular Tags