View Javadoc

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 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         // 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 &lt;a&gt; element, which would be created if it did
323      * not exist, then the next child &lt;b&gt; and so on until finally a
324      * &lt;c&gt; 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  */