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  */