1
2
3
4
5
6
7
8 package org.dom4j;
9
10 import java.io.StringReader;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.StringTokenizer;
14
15 import org.dom4j.io.SAXReader;
16 import org.dom4j.rule.Pattern;
17
18 import org.jaxen.VariableContext;
19
20 import org.xml.sax.InputSource;
21
22 /***
23 * <p>
24 * <code>DocumentHelper</code> is a collection of helper methods for using
25 * DOM4J.
26 * </p>
27 *
28 * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
29 * @version $Revision: 1.26 $
30 */
31 public final class DocumentHelper {
32 private DocumentHelper() {
33 }
34
35 private static DocumentFactory getDocumentFactory() {
36 return DocumentFactory.getInstance();
37 }
38
39
40 public static Document createDocument() {
41 return getDocumentFactory().createDocument();
42 }
43
44 public static Document createDocument(Element rootElement) {
45 return getDocumentFactory().createDocument(rootElement);
46 }
47
48 public static Element createElement(QName qname) {
49 return getDocumentFactory().createElement(qname);
50 }
51
52 public static Element createElement(String name) {
53 return getDocumentFactory().createElement(name);
54 }
55
56 public static Attribute createAttribute(Element owner, QName qname,
57 String value) {
58 return getDocumentFactory().createAttribute(owner, qname, value);
59 }
60
61 public static Attribute createAttribute(Element owner, String name,
62 String value) {
63 return getDocumentFactory().createAttribute(owner, name, value);
64 }
65
66 public static CDATA createCDATA(String text) {
67 return DocumentFactory.getInstance().createCDATA(text);
68 }
69
70 public static Comment createComment(String text) {
71 return DocumentFactory.getInstance().createComment(text);
72 }
73
74 public static Text createText(String text) {
75 return DocumentFactory.getInstance().createText(text);
76 }
77
78 public static Entity createEntity(String name, String text) {
79 return DocumentFactory.getInstance().createEntity(name, text);
80 }
81
82 public static Namespace createNamespace(String prefix, String uri) {
83 return DocumentFactory.getInstance().createNamespace(prefix, uri);
84 }
85
86 public static ProcessingInstruction createProcessingInstruction(String pi,
87 String d) {
88 return getDocumentFactory().createProcessingInstruction(pi, d);
89 }
90
91 public static ProcessingInstruction createProcessingInstruction(String pi,
92 Map data) {
93 return getDocumentFactory().createProcessingInstruction(pi, data);
94 }
95
96 public static QName createQName(String localName, Namespace namespace) {
97 return getDocumentFactory().createQName(localName, namespace);
98 }
99
100 public static QName createQName(String localName) {
101 return getDocumentFactory().createQName(localName);
102 }
103
104 /***
105 * <p>
106 * <code>createXPath</code> parses an XPath expression and creates a new
107 * XPath <code>XPath</code> instance using the singleton {@link
108 * DocumentFactory}.
109 * </p>
110 *
111 * @param xpathExpression
112 * is the XPath expression to create
113 *
114 * @return a new <code>XPath</code> instance
115 *
116 * @throws InvalidXPathException
117 * if the XPath expression is invalid
118 */
119 public static XPath createXPath(String xpathExpression)
120 throws InvalidXPathException {
121 return getDocumentFactory().createXPath(xpathExpression);
122 }
123
124 /***
125 * <p>
126 * <code>createXPath</code> parses an XPath expression and creates a new
127 * XPath <code>XPath</code> instance using the singleton {@link
128 * DocumentFactory}.
129 * </p>
130 *
131 * @param xpathExpression
132 * is the XPath expression to create
133 * @param context
134 * is the variable context to use when evaluating the XPath
135 *
136 * @return a new <code>XPath</code> instance
137 *
138 * @throws InvalidXPathException
139 * if the XPath expression is invalid
140 */
141 public static XPath createXPath(String xpathExpression,
142 VariableContext context) throws InvalidXPathException {
143 return getDocumentFactory().createXPath(xpathExpression, context);
144 }
145
146 /***
147 * <p>
148 * <code>createXPathFilter</code> parses a NodeFilter from the given XPath
149 * filter expression using the singleton {@link DocumentFactory}. XPath
150 * filter expressions occur within XPath expressions such as
151 * <code>self::node()[ filterExpression ]</code>
152 * </p>
153 *
154 * @param xpathFilterExpression
155 * is the XPath filter expression to create
156 *
157 * @return a new <code>NodeFilter</code> instance
158 */
159 public static NodeFilter createXPathFilter(String xpathFilterExpression) {
160 return getDocumentFactory().createXPathFilter(xpathFilterExpression);
161 }
162
163 /***
164 * <p>
165 * <code>createPattern</code> parses the given XPath expression to create
166 * an XSLT style {@link Pattern}instance which can then be used in an XSLT
167 * processing model.
168 * </p>
169 *
170 * @param xpathPattern
171 * is the XPath pattern expression to create
172 *
173 * @return a new <code>Pattern</code> instance
174 */
175 public static Pattern createPattern(String xpathPattern) {
176 return getDocumentFactory().createPattern(xpathPattern);
177 }
178
179 /***
180 * <p>
181 * <code>selectNodes</code> performs the given XPath expression on the
182 * {@link List}of {@link Node}instances appending all the results together
183 * into a single list.
184 * </p>
185 *
186 * @param xpathFilterExpression
187 * is the XPath filter expression to evaluate
188 * @param nodes
189 * is the list of nodes on which to evalute the XPath
190 *
191 * @return the results of all the XPath evaluations as a single list
192 */
193 public static List selectNodes(String xpathFilterExpression, List nodes) {
194 XPath xpath = createXPath(xpathFilterExpression);
195
196 return xpath.selectNodes(nodes);
197 }
198
199 /***
200 * <p>
201 * <code>selectNodes</code> performs the given XPath expression on the
202 * {@link List}of {@link Node}instances appending all the results together
203 * into a single list.
204 * </p>
205 *
206 * @param xpathFilterExpression
207 * is the XPath filter expression to evaluate
208 * @param node
209 * is the Node on which to evalute the XPath
210 *
211 * @return the results of all the XPath evaluations as a single list
212 */
213 public static List selectNodes(String xpathFilterExpression, Node node) {
214 XPath xpath = createXPath(xpathFilterExpression);
215
216 return xpath.selectNodes(node);
217 }
218
219 /***
220 * <p>
221 * <code>sort</code> sorts the given List of Nodes using an XPath
222 * expression as a {@link java.util.Comparator}.
223 * </p>
224 *
225 * @param list
226 * is the list of Nodes to sort
227 * @param xpathExpression
228 * is the XPath expression used for comparison
229 */
230 public static void sort(List list, String xpathExpression) {
231 XPath xpath = createXPath(xpathExpression);
232 xpath.sort(list);
233 }
234
235 /***
236 * <p>
237 * <code>sort</code> sorts the given List of Nodes using an XPath
238 * expression as a {@link java.util.Comparator}and optionally removing
239 * duplicates.
240 * </p>
241 *
242 * @param list
243 * is the list of Nodes to sort
244 * @param expression
245 * is the XPath expression used for comparison
246 * @param distinct
247 * if true then duplicate values (using the sortXPath for
248 * comparisions) will be removed from the List
249 */
250 public static void sort(List list, String expression, boolean distinct) {
251 XPath xpath = createXPath(expression);
252 xpath.sort(list, distinct);
253 }
254
255 /***
256 * <p>
257 * <code>parseText</code> parses the given text as an XML document and
258 * returns the newly created Document.
259 * </p>
260 *
261 * @param text
262 * the XML text to be parsed
263 *
264 * @return a newly parsed Document
265 *
266 * @throws DocumentException
267 * if the document could not be parsed
268 */
269 public static Document parseText(String text) throws DocumentException {
270 Document result = null;
271
272 SAXReader reader = new SAXReader();
273 String encoding = getEncoding(text);
274
275 InputSource source = new InputSource(new StringReader(text));
276 source.setEncoding(encoding);
277
278 result = reader.read(source);
279
280
281
282 if (result.getXMLEncoding() == null) {
283 result.setXMLEncoding(encoding);
284 }
285
286 return result;
287 }
288
289 private static String getEncoding(String text) {
290 String result = null;
291
292 String xml = text.trim();
293
294 if (xml.startsWith("<?xml")) {
295 int end = xml.indexOf("?>");
296 String sub = xml.substring(0, end);
297 StringTokenizer tokens = new StringTokenizer(sub, " =\"\'");
298
299 while (tokens.hasMoreTokens()) {
300 String token = tokens.nextToken();
301
302 if ("encoding".equals(token)) {
303 if (tokens.hasMoreTokens()) {
304 result = tokens.nextToken();
305 }
306
307 break;
308 }
309 }
310 }
311
312 return result;
313 }
314
315 /***
316 * <p>
317 * makeElement
318 * </p>
319 * a helper method which navigates from the given Document or Element node
320 * to some Element using the path expression, creating any necessary
321 * elements along the way. For example the path <code>a/b/c</code> would
322 * get the first child <a> element, which would be created if it did
323 * not exist, then the next child <b> and so on until finally a
324 * <c> element is returned.
325 *
326 * @param source
327 * is the Element or Document to start navigating from
328 * @param path
329 * is a simple path expression, seperated by '/' which denotes
330 * the path from the source to the resulting element such as
331 * a/b/c
332 *
333 * @return the first Element on the given path which either already existed
334 * on the path or were created by this method.
335 */
336 public static Element makeElement(Branch source, String path) {
337 StringTokenizer tokens = new StringTokenizer(path, "/");
338 Element parent;
339
340 if (source instanceof Document) {
341 Document document = (Document) source;
342 parent = document.getRootElement();
343
344
345
346 String name = tokens.nextToken();
347
348 if (parent == null) {
349 parent = document.addElement(name);
350 }
351 } else {
352 parent = (Element) source;
353 }
354
355 Element element = null;
356
357 while (tokens.hasMoreTokens()) {
358 String name = tokens.nextToken();
359
360 if (name.indexOf(':') > 0) {
361 element = parent.element(parent.getQName(name));
362 } else {
363 element = parent.element(name);
364 }
365
366 if (element == null) {
367 element = parent.addElement(name);
368 }
369
370 parent = element;
371 }
372
373 return element;
374 }
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412