0
|
1 /*--
|
|
2
|
|
3 $Id: XPath.java,v 1.17 2007/11/10 05:29:02 jhunter Exp $
|
|
4
|
|
5 Copyright (C) 2000-2007 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.xpath;
|
|
58
|
|
59
|
|
60 import java.io.*;
|
|
61 import java.lang.reflect.*;
|
|
62 import java.util.*;
|
|
63
|
|
64 import org.jdom.*;
|
|
65
|
|
66
|
|
67 /**
|
|
68 * A utility class for performing XPath calls on JDOM nodes, with a factory
|
|
69 * interface for obtaining a first XPath instance. Users operate against this
|
|
70 * class while XPath vendors can plug-in implementations underneath. Users
|
|
71 * can choose an implementation using either {@link #setXPathClass} or
|
|
72 * the system property "org.jdom.xpath.class".
|
|
73 *
|
|
74 * @version $Revision: 1.17 $, $Date: 2007/11/10 05:29:02 $
|
|
75 * @author Laurent Bihanic
|
|
76 */
|
|
77 public abstract class XPath implements Serializable {
|
|
78
|
|
79 private static final String CVS_ID =
|
|
80 "@(#) $RCSfile: XPath.java,v $ $Revision: 1.17 $ $Date: 2007/11/10 05:29:02 $ $Name: jdom_1_1_1 $";
|
|
81
|
|
82 /**
|
|
83 * The name of the system property from which to retrieve the
|
|
84 * name of the implementation class to use.
|
|
85 * <p>
|
|
86 * The property name is:
|
|
87 * "<code>org.jdom.xpath.class</code>".</p>
|
|
88 */
|
|
89 private final static String XPATH_CLASS_PROPERTY = "org.jdom.xpath.class";
|
|
90
|
|
91 /**
|
|
92 * The default implementation class to use if none was configured.
|
|
93 */
|
|
94 private final static String DEFAULT_XPATH_CLASS =
|
|
95 "org.jdom.xpath.JaxenXPath";
|
|
96
|
|
97 /**
|
|
98 * The string passable to the JAXP 1.3 XPathFactory isObjectModelSupported()
|
|
99 * method to query an XPath engine regarding its support for JDOM. Defined
|
|
100 * to be the well-known URI "http://jdom.org/jaxp/xpath/jdom".
|
|
101 */
|
|
102 public final static String JDOM_OBJECT_MODEL_URI =
|
|
103 "http://jdom.org/jaxp/xpath/jdom";
|
|
104
|
|
105 /**
|
|
106 * The constructor to instanciate a new XPath concrete
|
|
107 * implementation.
|
|
108 *
|
|
109 * @see #newInstance
|
|
110 */
|
|
111 private static Constructor constructor = null;
|
|
112
|
|
113 /**
|
|
114 * Creates a new XPath wrapper object, compiling the specified
|
|
115 * XPath expression.
|
|
116 *
|
|
117 * @param path the XPath expression to wrap.
|
|
118 *
|
|
119 * @throws JDOMException if the XPath expression is invalid.
|
|
120 */
|
|
121 public static XPath newInstance(String path) throws JDOMException {
|
|
122 try {
|
|
123 if (constructor == null) {
|
|
124 // First call => Determine implementation.
|
|
125 String className;
|
|
126 try {
|
|
127 className = System.getProperty(XPATH_CLASS_PROPERTY,
|
|
128 DEFAULT_XPATH_CLASS);
|
|
129 }
|
|
130 catch (SecurityException ex1) {
|
|
131 // Access to system property denied. => Use default impl.
|
|
132 className = DEFAULT_XPATH_CLASS;
|
|
133 }
|
|
134 setXPathClass(Class.forName(className));
|
|
135 }
|
|
136 // Allocate and return new implementation instance.
|
|
137 return (XPath)constructor.newInstance(new Object[] { path });
|
|
138 }
|
|
139 catch (JDOMException ex1) {
|
|
140 throw ex1;
|
|
141 }
|
|
142 catch (InvocationTargetException ex2) {
|
|
143 // Constructor threw an error on invocation.
|
|
144 Throwable t = ex2.getTargetException();
|
|
145
|
|
146 throw (t instanceof JDOMException)? (JDOMException)t:
|
|
147 new JDOMException(t.toString(), t);
|
|
148 }
|
|
149 catch (Exception ex3) {
|
|
150 // Any reflection error (probably due to a configuration mistake).
|
|
151 throw new JDOMException(ex3.toString(), ex3);
|
|
152 }
|
|
153 }
|
|
154
|
|
155 /**
|
|
156 * Sets the concrete XPath subclass to use when allocating XPath
|
|
157 * instances.
|
|
158 *
|
|
159 * @param aClass the concrete subclass of XPath.
|
|
160 *
|
|
161 * @throws IllegalArgumentException if <code>aClass</code> is
|
|
162 * <code>null</code>.
|
|
163 * @throws JDOMException if <code>aClass</code> is
|
|
164 * not a concrete subclass
|
|
165 * of XPath.
|
|
166 */
|
|
167 public static void setXPathClass(Class aClass) throws JDOMException {
|
|
168 if (aClass == null) {
|
|
169 throw new IllegalArgumentException("aClass");
|
|
170 }
|
|
171
|
|
172 try {
|
|
173 if ((XPath.class.isAssignableFrom(aClass)) &&
|
|
174 (Modifier.isAbstract(aClass.getModifiers()) == false)) {
|
|
175 // Concrete subclass of XPath => Get constructor
|
|
176 constructor = aClass.getConstructor(new Class[] { String.class });
|
|
177 }
|
|
178 else {
|
|
179 throw new JDOMException(aClass.getName() +
|
|
180 " is not a concrete JDOM XPath implementation");
|
|
181 }
|
|
182 }
|
|
183 catch (JDOMException ex1) {
|
|
184 throw ex1;
|
|
185 }
|
|
186 catch (Exception ex2) {
|
|
187 // Any reflection error (probably due to a configuration mistake).
|
|
188 throw new JDOMException(ex2.toString(), ex2);
|
|
189 }
|
|
190 }
|
|
191
|
|
192 /**
|
|
193 * Evaluates the wrapped XPath expression and returns the list
|
|
194 * of selected items.
|
|
195 *
|
|
196 * @param context the node to use as context for evaluating
|
|
197 * the XPath expression.
|
|
198 *
|
|
199 * @return the list of selected items, which may be of types: {@link Element},
|
|
200 * {@link Attribute}, {@link Text}, {@link CDATA},
|
|
201 * {@link Comment}, {@link ProcessingInstruction}, Boolean,
|
|
202 * Double, or String.
|
|
203 *
|
|
204 * @throws JDOMException if the evaluation of the XPath
|
|
205 * expression on the specified context
|
|
206 * failed.
|
|
207 */
|
|
208 abstract public List selectNodes(Object context) throws JDOMException;
|
|
209
|
|
210 /**
|
|
211 * Evaluates the wrapped XPath expression and returns the first
|
|
212 * entry in the list of selected nodes (or atomics).
|
|
213 *
|
|
214 * @param context the node to use as context for evaluating
|
|
215 * the XPath expression.
|
|
216 *
|
|
217 * @return the first selected item, which may be of types: {@link Element},
|
|
218 * {@link Attribute}, {@link Text}, {@link CDATA},
|
|
219 * {@link Comment}, {@link ProcessingInstruction}, Boolean,
|
|
220 * Double, String, or <code>null</code> if no item was selected.
|
|
221 *
|
|
222 * @throws JDOMException if the evaluation of the XPath
|
|
223 * expression on the specified context
|
|
224 * failed.
|
|
225 */
|
|
226 abstract public Object selectSingleNode(Object context) throws JDOMException;
|
|
227
|
|
228 /**
|
|
229 * Returns the string value of the first node selected by applying
|
|
230 * the wrapped XPath expression to the given context.
|
|
231 *
|
|
232 * @param context the element to use as context for evaluating
|
|
233 * the XPath expression.
|
|
234 *
|
|
235 * @return the string value of the first node selected by applying
|
|
236 * the wrapped XPath expression to the given context.
|
|
237 *
|
|
238 * @throws JDOMException if the XPath expression is invalid or
|
|
239 * its evaluation on the specified context
|
|
240 * failed.
|
|
241 */
|
|
242 abstract public String valueOf(Object context) throws JDOMException;
|
|
243
|
|
244 /**
|
|
245 * Returns the number value of the first node selected by applying
|
|
246 * the wrapped XPath expression to the given context.
|
|
247 *
|
|
248 * @param context the element to use as context for evaluating
|
|
249 * the XPath expression.
|
|
250 *
|
|
251 * @return the number value of the first node selected by applying
|
|
252 * the wrapped XPath expression to the given context,
|
|
253 * <code>null</code> if no node was selected or the
|
|
254 * special value {@link java.lang.Double#NaN}
|
|
255 * (Not-a-Number) if the selected value can not be
|
|
256 * converted into a number value.
|
|
257 *
|
|
258 * @throws JDOMException if the XPath expression is invalid or
|
|
259 * its evaluation on the specified context
|
|
260 * failed.
|
|
261 */
|
|
262 abstract public Number numberValueOf(Object context) throws JDOMException;
|
|
263
|
|
264 /**
|
|
265 * Defines an XPath variable and sets its value.
|
|
266 *
|
|
267 * @param name the variable name.
|
|
268 * @param value the variable value.
|
|
269 *
|
|
270 * @throws IllegalArgumentException if <code>name</code> is not
|
|
271 * a valid XPath variable name
|
|
272 * or if the value type is not
|
|
273 * supported by the underlying
|
|
274 * implementation
|
|
275 */
|
|
276 abstract public void setVariable(String name, Object value);
|
|
277
|
|
278 /**
|
|
279 * Adds a namespace definition to the list of namespaces known of
|
|
280 * this XPath expression.
|
|
281 * <p>
|
|
282 * <strong>Note</strong>: In XPath, there is no such thing as a
|
|
283 * 'default namespace'. The empty prefix <b>always</b> resolves
|
|
284 * to the empty namespace URI.</p>
|
|
285 *
|
|
286 * @param namespace the namespace.
|
|
287 */
|
|
288 abstract public void addNamespace(Namespace namespace);
|
|
289
|
|
290 /**
|
|
291 * Adds a namespace definition (prefix and URI) to the list of
|
|
292 * namespaces known of this XPath expression.
|
|
293 * <p>
|
|
294 * <strong>Note</strong>: In XPath, there is no such thing as a
|
|
295 * 'default namespace'. The empty prefix <b>always</b> resolves
|
|
296 * to the empty namespace URI.</p>
|
|
297 *
|
|
298 * @param prefix the namespace prefix.
|
|
299 * @param uri the namespace URI.
|
|
300 *
|
|
301 * @throws IllegalNameException if the prefix or uri are null or
|
|
302 * empty strings or if they contain
|
|
303 * illegal characters.
|
|
304 */
|
|
305 public void addNamespace(String prefix, String uri) {
|
|
306 addNamespace(Namespace.getNamespace(prefix, uri));
|
|
307 }
|
|
308
|
|
309 /**
|
|
310 * Returns the wrapped XPath expression as a string.
|
|
311 *
|
|
312 * @return the wrapped XPath expression as a string.
|
|
313 */
|
|
314 abstract public String getXPath();
|
|
315
|
|
316
|
|
317 /**
|
|
318 * Evaluates an XPath expression and returns the list of selected
|
|
319 * items.
|
|
320 * <p>
|
|
321 * <strong>Note</strong>: This method should not be used when the
|
|
322 * same XPath expression needs to be applied several times (on the
|
|
323 * same or different contexts) as it requires the expression to be
|
|
324 * compiled before being evaluated. In such cases,
|
|
325 * {@link #newInstance allocating} an XPath wrapper instance and
|
|
326 * {@link #selectNodes(java.lang.Object) evaluating} it several
|
|
327 * times is way more efficient.
|
|
328 * </p>
|
|
329 *
|
|
330 * @param context the node to use as context for evaluating
|
|
331 * the XPath expression.
|
|
332 * @param path the XPath expression to evaluate.
|
|
333 *
|
|
334 * @return the list of selected items, which may be of types: {@link Element},
|
|
335 * {@link Attribute}, {@link Text}, {@link CDATA},
|
|
336 * {@link Comment}, {@link ProcessingInstruction}, Boolean,
|
|
337 * Double, or String.
|
|
338 *
|
|
339 * @throws JDOMException if the XPath expression is invalid or
|
|
340 * its evaluation on the specified context
|
|
341 * failed.
|
|
342 */
|
|
343 public static List selectNodes(Object context, String path)
|
|
344 throws JDOMException {
|
|
345 return newInstance(path).selectNodes(context);
|
|
346 }
|
|
347
|
|
348 /**
|
|
349 * Evaluates the wrapped XPath expression and returns the first
|
|
350 * entry in the list of selected nodes (or atomics).
|
|
351 * <p>
|
|
352 * <strong>Note</strong>: This method should not be used when the
|
|
353 * same XPath expression needs to be applied several times (on the
|
|
354 * same or different contexts) as it requires the expression to be
|
|
355 * compiled before being evaluated. In such cases,
|
|
356 * {@link #newInstance allocating} an XPath wrapper instance and
|
|
357 * {@link #selectSingleNode(java.lang.Object) evaluating} it
|
|
358 * several times is way more efficient.
|
|
359 * </p>
|
|
360 *
|
|
361 * @param context the element to use as context for evaluating
|
|
362 * the XPath expression.
|
|
363 * @param path the XPath expression to evaluate.
|
|
364 *
|
|
365 * @return the first selected item, which may be of types: {@link Element},
|
|
366 * {@link Attribute}, {@link Text}, {@link CDATA},
|
|
367 * {@link Comment}, {@link ProcessingInstruction}, Boolean,
|
|
368 * Double, String, or <code>null</code> if no item was selected.
|
|
369 *
|
|
370 * @throws JDOMException if the XPath expression is invalid or
|
|
371 * its evaluation on the specified context
|
|
372 * failed.
|
|
373 */
|
|
374 public static Object selectSingleNode(Object context, String path)
|
|
375 throws JDOMException {
|
|
376 return newInstance(path).selectSingleNode(context);
|
|
377 }
|
|
378
|
|
379
|
|
380 //-------------------------------------------------------------------------
|
|
381 // Serialization support
|
|
382 //-------------------------------------------------------------------------
|
|
383
|
|
384 /**
|
|
385 * <i>[Serialization support]</i> Returns the alternative object
|
|
386 * to write to the stream when serializing this object. This
|
|
387 * method returns an instance of a dedicated nested class to
|
|
388 * serialize XPath expressions independently of the concrete
|
|
389 * implementation being used.
|
|
390 * <p>
|
|
391 * <strong>Note</strong>: Subclasses are not allowed to override
|
|
392 * this method to ensure valid serialization of all
|
|
393 * implementations.</p>
|
|
394 *
|
|
395 * @return an XPathString instance configured with the wrapped
|
|
396 * XPath expression.
|
|
397 *
|
|
398 * @throws ObjectStreamException never.
|
|
399 */
|
|
400 protected final Object writeReplace() throws ObjectStreamException {
|
|
401 return new XPathString(this.getXPath());
|
|
402 }
|
|
403
|
|
404 /**
|
|
405 * The XPathString is dedicated to serialize instances of
|
|
406 * XPath subclasses in a implementation-independent manner.
|
|
407 * <p>
|
|
408 * XPathString ensures that only string data are serialized. Upon
|
|
409 * deserialization, XPathString relies on XPath factory method to
|
|
410 * to create instances of the concrete XPath wrapper currently
|
|
411 * configured.</p>
|
|
412 */
|
|
413 private final static class XPathString implements Serializable {
|
|
414 /**
|
|
415 * The XPath expression as a string.
|
|
416 */
|
|
417 private String xPath = null;
|
|
418
|
|
419 /**
|
|
420 * Creates a new XPathString instance from the specified
|
|
421 * XPath expression.
|
|
422 *
|
|
423 * @param xpath the XPath expression.
|
|
424 */
|
|
425 public XPathString(String xpath) {
|
|
426 super();
|
|
427
|
|
428 this.xPath = xpath;
|
|
429 }
|
|
430
|
|
431 /**
|
|
432 * <i>[Serialization support]</i> Resolves the read XPathString
|
|
433 * objects into XPath implementations.
|
|
434 *
|
|
435 * @return an instance of a concrete implementation of
|
|
436 * XPath.
|
|
437 *
|
|
438 * @throws ObjectStreamException if no XPath could be built
|
|
439 * from the read object.
|
|
440 */
|
|
441 private Object readResolve() throws ObjectStreamException {
|
|
442 try {
|
|
443 return XPath.newInstance(this.xPath);
|
|
444 }
|
|
445 catch (JDOMException ex1) {
|
|
446 throw new InvalidObjectException(
|
|
447 "Can't create XPath object for expression \"" +
|
|
448 this.xPath + "\": " + ex1.toString());
|
|
449 }
|
|
450 }
|
|
451 }
|
|
452 }
|
|
453
|