1 /*
2 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3 *
4 * This software is open source.
5 * See the bottom of this file for the licence.
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 // Static helper methods
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<? extends Node> selectNodes(String xpathFilterExpression, List<? extends Node> 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<? extends Node> 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<? extends Node> 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<? extends Node> 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 // if the XML parser doesn't provide a way to retrieve the encoding,
281 // specify it manually
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 // lets throw a NoSuchElementException
345 // if we are given an empty path
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 * Redistribution and use of this software and associated documentation
379 * ("Software"), with or without modification, are permitted provided that the
380 * following conditions are met:
381 *
382 * 1. Redistributions of source code must retain copyright statements and
383 * notices. Redistributions must also contain a copy of this document.
384 *
385 * 2. Redistributions in binary form must reproduce the above copyright notice,
386 * this list of conditions and the following disclaimer in the documentation
387 * and/or other materials provided with the distribution.
388 *
389 * 3. The name "DOM4J" must not be used to endorse or promote products derived
390 * from this Software without prior written permission of MetaStuff, Ltd. For
391 * written permission, please contact dom4j-info@metastuff.com.
392 *
393 * 4. Products derived from this Software may not be called "DOM4J" nor may
394 * "DOM4J" appear in their names without prior written permission of MetaStuff,
395 * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
396 *
397 * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
398 *
399 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
400 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
401 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
402 * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
403 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
404 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
405 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
406 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
407 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
408 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
409 * POSSIBILITY OF SUCH DAMAGE.
410 *
411 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
412 */