KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > contrib > helpers > XPathHelper


1 /*--
2
3  $Id: XPathHelper.java,v 1.5 2004/09/03 06:37:29 jhunter Exp $
4
5  Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom.contrib.helpers;
58
59
60 import java.util.List JavaDoc;
61 import java.util.Iterator JavaDoc;
62
63 import org.jdom.*;
64 import org.jdom.filter.ContentFilter;
65
66
67 /**
68  * Provides a set of utility methods to generate XPath expressions
69  * to select a given node in a (subtree of a) document.
70  * <p>
71  * <strong>Note</strong>: As this class has no knowledge of the
72  * document content, the generated XPath expression rely on the
73  * document structure. Hence any modification of the structure
74  * of the document may invalidate the generated XPaths.</p>
75  *
76  * @author Laurent Bihanic
77  */

78 public class XPathHelper {
79
80     /**
81      * Returns the path to the specified Element from the document
82      * root as an XPath expression.
83      *
84      * @param to the Element the generated path shall select.
85      *
86      * @return an XPath expression to select the specified node.
87      *
88      * @throws JDOMException if the XPath generation failed.
89      * @throws IllegalArgumentException if <code>to</code> is
90      * <code>null</code>.
91      */

92     public static String JavaDoc getPathString(Element to) throws JDOMException {
93         return getPathString(null, to);
94     }
95
96     /**
97      * Returns the path from a given JDOM node to the specified
98      * Element as an XPath expression.
99      *
100      * @param from the Document or Element node at which the
101      * the generated path shall be applied. Use
102      * <code>null</code> to specify the topmost
103      * ancestor of the <code>to</code> node.
104      * @param to the Element the generated path shall select.
105      *
106      * @return an XPath expression to select the specified node.
107      *
108      * @throws JDOMException if the XPath generation failed.
109      * @throws IllegalArgumentException if <code>to</code> is
110      * <code>null</code> or <code>from</code> is not
111      * a {@link Document} or a {@link Element} node.
112      */

113     public static String JavaDoc getPathString(Object JavaDoc from, Element to)
114                                                 throws JDOMException {
115         checkPathStringArguments(from, to);
116
117         if (to == from) {
118             return "node()";
119         }
120         else {
121             return getElementPath(from, to, true).toString();
122         }
123     }
124
125     /**
126      * Returns the path to the specified Attribute from the document
127      * root as an XPath expression.
128      *
129      * @param to the Attribute the generated path shall select.
130      *
131      * @return an XPath expression to select the specified node.
132      *
133      * @throws JDOMException if the XPath generation failed.
134      * @throws IllegalArgumentException if <code>to</code> is
135      * <code>null</code>.
136      */

137     public static String JavaDoc getPathString(Attribute to) throws JDOMException {
138         return getPathString(null, to);
139     }
140
141     /**
142      * Returns the path from a given JDOM node to the specified
143      * Attribute as an XPath expression.
144      *
145      * @param from the Document or Element node at which the
146      * the generated path shall be applied. Use
147      * <code>null</code> to specify the topmost
148      * ancestor of the <code>to</code> node.
149      * @param to the Attribute the generated path shall select.
150      *
151      * @return an XPath expression to select the specified node.
152      *
153      * @throws JDOMException if the XPath generation failed.
154      * @throws IllegalArgumentException if <code>to</code> is
155      * <code>null</code> or <code>from</code> is not
156      * a {@link Document} or a {@link Element} node.
157      */

158     public static String JavaDoc getPathString(Object JavaDoc from, Attribute to)
159                                                         throws JDOMException {
160         checkPathStringArguments(from, to);
161
162         if (to == from) {
163             return "node()";
164         }
165         else {
166             StringBuffer JavaDoc path = getElementPath(from, to.getParent(), false);
167             path.append('@').append(to.getName()).toString();
168             return path.toString();
169         }
170     }
171
172     /**
173      * Returns the path to the specified Text node from the document
174      * root as an XPath expression.
175      *
176      * @param to the Text node the generated path shall select.
177      *
178      * @return an XPath expression to select the specified node.
179      *
180      * @throws JDOMException if the XPath generation failed.
181      * @throws IllegalArgumentException if <code>to</code> is
182      * <code>null</code>.
183      */

184     public static String JavaDoc getPathString(Text to) throws JDOMException {
185         return getPathString(null, to);
186     }
187
188     /**
189      * Returns the path from a given JDOM node to the specified
190      * Text node as an XPath expression.
191      *
192      * @param from the Document or Element node at which the
193      * the generated path shall be applied. Use
194      * <code>null</code> to specify the topmost
195      * ancestor of the <code>to</code> node.
196      * @param to the Text node the generated path shall select.
197      *
198      * @return an XPath expression to select the specified node.
199      *
200      * @throws JDOMException if the XPath generation failed.
201      * @throws IllegalArgumentException if <code>to</code> is
202      * <code>null</code> or <code>from</code> is not
203      * a {@link Document} or a {@link Element} node.
204      */

205     public static String JavaDoc getPathString(Object JavaDoc from, Text to)
206                                                       throws JDOMException {
207         checkPathStringArguments(from, to);
208
209         if (to == from) {
210             return "node()";
211         }
212         else {
213             Element parent = to.getParentElement();
214             List JavaDoc siblings = null;
215             int nodeType = ContentFilter.TEXT;
216             StringBuffer JavaDoc path = getElementPath(from, parent, false);
217
218             if (parent != null) {
219                 siblings = parent.getContent(new ContentFilter(nodeType));
220             }
221             else {
222                 Document doc = to.getDocument();
223                 if (doc != null) {
224                     siblings = doc.getContent(new ContentFilter(nodeType));
225                 }
226             }
227             return getPositionPath(to, siblings, "text()", path).toString();
228         }
229     }
230
231     /**
232      * Returns the path to the specified Comment from the document
233      * root as an XPath expression.
234      *
235      * @param to the Comment the generated path shall select.
236      *
237      * @return an XPath expression to select the specified node.
238      *
239      * @throws JDOMException if the XPath generation failed.
240      * @throws IllegalArgumentException if <code>to</code> is
241      * <code>null</code>.
242      */

243     public static String JavaDoc getPathString(Comment to) throws JDOMException {
244         return getPathString(null, to);
245     }
246
247     /**
248      * Returns the path from a given JDOM node to the specified
249      * Comment as an XPath expression.
250      *
251      * @param from the Document or Element node at which the
252      * the generated path shall be applied. Use
253      * <code>null</code> to specify the topmost
254      * ancestor of the <code>to</code> node.
255      * @param to the Comment the generated path shall select.
256      *
257      * @return an XPath expression to select the specified node.
258      *
259      * @throws JDOMException if the XPath generation failed.
260      * @throws IllegalArgumentException if <code>to</code> is
261      * <code>null</code> or <code>from</code> is not
262      * a {@link Document} or a {@link Element} node.
263      */

264     public static String JavaDoc getPathString(Object JavaDoc from, Comment to)
265                                                   throws JDOMException {
266         checkPathStringArguments(from, to);
267
268         if (to == from) {
269             return "node()";
270         }
271         else {
272             Element parent = to.getParentElement();
273             List JavaDoc siblings = null;
274             int nodeType = ContentFilter.COMMENT;
275             StringBuffer JavaDoc path = getElementPath(from, parent, false);
276
277             if (parent != null) {
278                 siblings = parent.getContent(new ContentFilter(nodeType));
279             }
280             else {
281                 Document doc = to.getDocument();
282                 if (doc != null) {
283                     siblings = doc.getContent(new ContentFilter(nodeType));
284                 }
285             }
286             return getPositionPath(to, siblings, "comment()", path).toString();
287         }
288     }
289
290     /**
291      * Returns the path to the specified ProcessingInstruction node
292      * from the document root as an XPath expression.
293      *
294      * @param to the ProcessingInstruction node the generated path
295      * shall select.
296      *
297      * @return an XPath expression to select the specified node.
298      *
299      * @throws JDOMException if the XPath generation failed.
300      * @throws IllegalArgumentException if <code>to</code> is
301      * <code>null</code>.
302      */

303     public static String JavaDoc getPathString(ProcessingInstruction to)
304                                             throws JDOMException {
305         return getPathString(null, to);
306     }
307
308     /**
309      * Returns the path from a given JDOM node to the specified
310      * ProcessingInstruction node as an XPath expression.
311      *
312      * @param from the Document or Element node at which the
313      * the generated path shall be applied. Use
314      * <code>null</code> to specify the topmost
315      * ancestor of the <code>to</code> node.
316      * @param to the ProcessingInstruction node the generated
317      * path shall select.
318      *
319      * @return an XPath expression to select the specified node.
320      *
321      * @throws JDOMException if the XPath generation failed.
322      * @throws IllegalArgumentException if <code>to</code> is
323      * <code>null</code> or <code>from</code> is not
324      * a {@link Document} or a {@link Element} node.
325      */

326     public static String JavaDoc getPathString(Object JavaDoc from, ProcessingInstruction to)
327                                                  throws JDOMException {
328         checkPathStringArguments(from, to);
329
330         if (to == from) {
331             return "node()";
332         }
333         else {
334             Element parent = to.getParentElement();
335             List JavaDoc siblings = null;
336             int nodeType = ContentFilter.PI;
337             StringBuffer JavaDoc path = getElementPath(from, parent, false);
338
339             if (parent != null) {
340                 siblings = parent.getContent(new ContentFilter(nodeType));
341             }
342             else {
343                 Document doc = to.getDocument();
344                 if (doc != null) {
345                     siblings = doc.getContent(new ContentFilter(nodeType));
346                 }
347             }
348             return getPositionPath(to, siblings,
349                                    "processing-instruction()", path).toString();
350         }
351     }
352
353     /**
354      * Returns the path to the specified JDOM node from the document
355      * root as an XPath expression.
356      *
357      * @param to the JDOM node the generated path shall select.
358      *
359      * @return an XPath expression to select the specified node.
360      *
361      * @throws JDOMException if the XPath generation failed.
362      * @throws IllegalArgumentException if <code>to</code> is
363      * <code>null</code> or is not a JDOM node selectable
364      * by XPath expressions (Element, Attribute, Text,
365      * Comment, ProcessingInstruction).
366      */

367     public static String JavaDoc getPathString(Object JavaDoc to) throws JDOMException {
368         return getPathString(null, to);
369     }
370
371     /**
372      * Returns the path from a JDOM node to another JDOM node
373      * as an XPath expression.
374      *
375      * @param from the Document or Element node at which the
376      * the generated path shall be applied. Use
377      * <code>null</code> to specify the topmost
378      * ancestor of the <code>to</code> node.
379      * @param to the JDOM node the generated path shall select.
380      *
381      * @return an XPath expression to select the specified node.
382      *
383      * @throws JDOMException if the XPath generation failed.
384      * @throws IllegalArgumentException if <code>from</code> is not
385      * a {@link Document} or a {@link Element} node or
386      * <code>to</code> is <code>null</code> or is not a JDOM
387      * node selectable by XPath expressions (Element,
388      * Attribute, Text, Comment, ProcessingInstruction).
389      */

390     public static String JavaDoc getPathString(Object JavaDoc from, Object JavaDoc to)
391             throws JDOMException {
392         if (to instanceof Element) {
393             return getPathString(from, (Element) to);
394         }
395         else if (to instanceof Attribute) {
396             return getPathString(from, (Attribute) to);
397         }
398         else if (to instanceof Text) {
399             return getPathString(from, (Text) to);
400         }
401         else if (to instanceof Comment) {
402             return getPathString(from, (Comment) to);
403         }
404         else if (to instanceof ProcessingInstruction) {
405             return getPathString(from, (ProcessingInstruction) to);
406         }
407         else {
408             throw new IllegalArgumentException JavaDoc(
409                     "\"to \" shall be an Element, Attribute," +
410                     " Text, Comment or ProcessingInstruction node");
411         }
412     }
413
414
415     /**
416      * Checks that the two arguments of a <code>getPathString()</code>
417      * call are valid.
418      *
419      * @param from the <code>from</code> node.
420      * @param to the <code>to</code> node.
421      *
422      * @throws IllegalArgumentException if one of the arguments is
423      * invalid.
424      */

425     private static void checkPathStringArguments(Object JavaDoc from, Object JavaDoc to) {
426         if (!((from == null) || (from instanceof Element)
427                 || (from instanceof Document))) {
428             throw new IllegalArgumentException JavaDoc("from");
429         }
430         if (to == null) {
431             throw new IllegalArgumentException JavaDoc("to");
432         }
433     }
434
435     /**
436      * Returns the XPath expression to select the <code>to</code>
437      * element relatively to the <code>from</code> element.
438      *
439      * @param from the <code>from</code> element.
440      * @param to the <code>to</code> element.
441      * @param leaf whether the <code>to</code> is the last element
442      * of the path to return.
443      *
444      * @return an XPath expression to select the <code>to</code>
445      * element.
446      *
447      * @throws JDOMException if the XPath generation failed.
448      */

449     private static StringBuffer JavaDoc getElementPath(Object JavaDoc from,
450                                                Element to, boolean leaf)
451                                                   throws JDOMException {
452         if (from instanceof Document) {
453             from = null;
454         }
455         return getElementPath((Element) from, to, leaf, new StringBuffer JavaDoc());
456     }
457
458     /**
459      * Returns the XPath expression to select the <code>to</code>
460      * element relatively to the <code>from</code> element.
461      *
462      * @param from the <code>from</code> element.
463      * @param to the <code>to</code> element.
464      * @param leaf whether the <code>to</code> is the last element
465      * of the path to return.
466      * @param path the buffer to which append the path.
467      *
468      * @return an XPath expression to select the <code>to</code>
469      * element.
470      *
471      * @throws JDOMException if the XPath generation failed.
472      */

473     private static StringBuffer JavaDoc getElementPath(Element from, Element to,
474                                                boolean leaf, StringBuffer JavaDoc path)
475                                                          throws JDOMException {
476         if (to != from) {
477             boolean isRoot = false;
478             List JavaDoc siblings = null;
479
480             Element parent = to.getParentElement();
481             if (parent == null) {
482                 // Oops! No more parent but I haven't yet reached the from node.
483
if (parent != from) {
484                     // Ouch! from node is not an ancestor.
485
throw new JDOMException(
486                      "The \"from\" node is not an ancestor of the \"to\" node");
487                 }
488                 if (to.isRootElement()) {
489                     isRoot = true;
490                     path.append('/');
491                 }
492             }
493             else {
494                 siblings = parent.getChildren(to.getName(), null);
495             }
496
497             if (parent != from) {
498                 path = getElementPath(from, parent, false, path);
499             }
500
501             Namespace ns = to.getNamespace();
502             if (ns == Namespace.NO_NAMESPACE) {
503                 // No namespace => Use local name only.
504
path = getPositionPath(to, siblings, to.getName(), path);
505             }
506             else {
507                 // Elements belongs to a namespace => Check prefix.
508
String JavaDoc prefix = to.getNamespacePrefix();
509                 if ("".equals(prefix)) {
510                     // No prefix (default namespace in scope
511
// => Use wildcard & local name combination.
512
path.append("*[local-name()='").
513                             append(to.getName()).append("']");
514
515                     path = getPositionPath(to, siblings, null, path);
516                 }
517                 else {
518                     // Not the default namespace => Use prefix.
519
path.append(to.getNamespacePrefix()).append(':');
520
521                     path = getPositionPath(to, siblings, to.getName(), path);
522                 }
523             }
524
525             if ((!leaf) && (path.length() != 0)) {
526                 path.append('/');
527             }
528         }
529         return (path);
530     }
531
532     /**
533      * Appends the specified path token to the provided buffer
534      * followed by the position specification of the target node in
535      * its siblings list.
536      *
537      * @param node the target node for the XPath expression.
538      * @param siblings the siblings of the target node.
539      * @param pathToken the path token identifying the target node.
540      * @param buffer the buffer to which appending the XPath
541      * sub-expression or <code>null</code> if the
542      * method shall allocate a new buffer.
543      *
544      * @return the XPath sub-expression to select the target node
545      * among its siblings.
546      */

547     private static StringBuffer JavaDoc getPositionPath(Object JavaDoc node, List JavaDoc siblings,
548                                                 String JavaDoc pathToken,
549                                                 StringBuffer JavaDoc buffer) {
550         if (buffer == null) {
551             buffer = new StringBuffer JavaDoc();
552         }
553         if (pathToken != null) {
554             buffer.append(pathToken);
555         }
556
557         if ((siblings != null) && (siblings.size() != 1)) {
558             int position = 0;
559             for (Iterator JavaDoc i = siblings.iterator(); i.hasNext();) {
560                 position++;
561                 if (i.next() == node) break;
562             }
563             buffer.append('[').append(position).append(']');
564         }
565         return buffer;
566     }
567 }
568
569
Popular Tags