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.IOException;
    11  import java.io.ObjectInputStream;
    12  import java.io.Serializable;
    13  import java.util.List;
    14  import java.util.Map;
    15
    16  import org.dom4j.rule.Pattern;
    17  import org.dom4j.tree.AbstractDocument;
    18  import org.dom4j.tree.DefaultAttribute;
    19  import org.dom4j.tree.DefaultCDATA;
    20  import org.dom4j.tree.DefaultComment;
    21  import org.dom4j.tree.DefaultDocument;
    22  import org.dom4j.tree.DefaultDocumentType;
    23  import org.dom4j.tree.DefaultElement;
    24  import org.dom4j.tree.DefaultEntity;
    25  import org.dom4j.tree.DefaultProcessingInstruction;
    26  import org.dom4j.tree.DefaultText;
    27  import org.dom4j.tree.QNameCache;
    28  import org.dom4j.util.SimpleSingleton;
    29  import org.dom4j.util.SingletonStrategy;
    30  import org.dom4j.xpath.DefaultXPath;
    31  import org.dom4j.xpath.XPathPattern;
    32  import org.jaxen.VariableContext;
    33
    34  /***
    35   * <p>
    36   * <code>DocumentFactory</code> is a collection of factory methods to allow
    37   * easy custom building of DOM4J trees. The default tree that is built uses a
    38   * doubly linked tree.
    39   * </p>
    40   *
    41   * <p>
    42   * The tree built allows full XPath expressions from anywhere on the tree.
    43   * </p>
    44   *
    45   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
    46   */
    47  public class DocumentFactory implements Serializable {
    48      private static SingletonStrategy singleton = null;
    49
    50      protected transient QNameCache cache;
    51
    52      /*** Default namespace prefix -> URI mappings for XPath expressions to use */
    53      private Map xpathNamespaceURIs;
    54
    55      private static SingletonStrategy createSingleton() {
    56          SingletonStrategy result = null;
    57
    58          String documentFactoryClassName;
    59          try {
    60              documentFactoryClassName = System.getProperty("org.dom4j.factory",
    61                      "org.dom4j.DocumentFactory");
    62          } catch (Exception e) {
    63              documentFactoryClassName = "org.dom4j.DocumentFactory";
    64          }
    65
    66          try {
    67              String singletonClass = System.getProperty(
    68                      "org.dom4j.DocumentFactory.singleton.strategy",
    69                      "org.dom4j.util.SimpleSingleton");
    70              Class clazz = Class.forName(singletonClass);
    71              result = (SingletonStrategy) clazz.newInstance();
    72          } catch (Exception e) {
    73              result = new SimpleSingleton();
    74          }
    75
    76          result.setSingletonClassName(documentFactoryClassName);
    77
    78          return result;
    79      }
    80
    81      public DocumentFactory() {
    82          init();
    83      }
    84
    85      /***
    86       * <p>
    87       * Access to singleton implementation of DocumentFactory which is used if no
    88       * DocumentFactory is specified when building using the standard builders.
    89       * </p>
    90       *
    91       * @return the default singleon instance
    92       */
    93      public static synchronized DocumentFactory getInstance() {
    94          if (singleton == null) {
    95              singleton = createSingleton();
    96          }
    97          return (DocumentFactory) singleton.instance();
    98      }
    99
    100     // Factory methods
    101     public Document createDocument() {
    102         DefaultDocument answer = new DefaultDocument();
    103         answer.setDocumentFactory(this);
    104
    105         return answer;
    106     }
    107
    108     /***
    109      * DOCUMENT ME!
    110      *
    111      * @param encoding
    112      *            DOCUMENT ME!
    113      *
    114      * @return DOCUMENT ME!
    115      *
    116      * @since 1.5
    117      */
    118     public Document createDocument(String encoding) {
    119         // to keep the DocumentFactory backwards compatible, we have to do this
    120         // in this not so nice way, since subclasses only need to extend the
    121         // createDocument() method.
    122         Document answer = createDocument();
    123
    124         if (answer instanceof AbstractDocument) {
    125             ((AbstractDocument) answer).setXMLEncoding(encoding);
    126         }
    127
    128         return answer;
    129     }
    130
    131     public Document createDocument(Element rootElement) {
    132         Document answer = createDocument();
    133         answer.setRootElement(rootElement);
    134
    135         return answer;
    136     }
    137
    138     public DocumentType createDocType(String name, String publicId,
    139             String systemId) {
    140         return new DefaultDocumentType(name, publicId, systemId);
    141     }
    142
    143     public Element createElement(QName qname) {
    144         return new DefaultElement(qname);
    145     }
    146
    147     public Element createElement(String name) {
    148         return createElement(createQName(name));
    149     }
    150
    151     public Element createElement(String qualifiedName, String namespaceURI) {
    152         return createElement(createQName(qualifiedName, namespaceURI));
    153     }
    154
    155     public Attribute createAttribute(Element owner, QName qname, String value) {
    156         return new DefaultAttribute(qname, value);
    157     }
    158
    159     public Attribute createAttribute(Element owner, String name, String value) {
    160         return createAttribute(owner, createQName(name), value);
    161     }
    162
    163     public CDATA createCDATA(String text) {
    164         return new DefaultCDATA(text);
    165     }
    166
    167     public Comment createComment(String text) {
    168         return new DefaultComment(text);
    169     }
    170
    171     public Text createText(String text) {
    172         if (text == null) {
    173             String msg = "Adding text to an XML document must not be null";
    174             throw new IllegalArgumentException(msg);
    175         }
    176
    177         return new DefaultText(text);
    178     }
    179
    180     public Entity createEntity(String name, String text) {
    181         return new DefaultEntity(name, text);
    182     }
    183
    184     public Namespace createNamespace(String prefix, String uri) {
    185         return Namespace.get(prefix, uri);
    186     }
    187
    188     public ProcessingInstruction createProcessingInstruction(String target,
    189             String data) {
    190         return new DefaultProcessingInstruction(target, data);
    191     }
    192
    193     public ProcessingInstruction createProcessingInstruction(String target,
    194             Map data) {
    195         return new DefaultProcessingInstruction(target, data);
    196     }
    197
    198     public QName createQName(String localName, Namespace namespace) {
    199         return cache.get(localName, namespace);
    200     }
    201
    202     public QName createQName(String localName) {
    203         return cache.get(localName);
    204     }
    205
    206     public QName createQName(String name, String prefix, String uri) {
    207         return cache.get(name, Namespace.get(prefix, uri));
    208     }
    209
    210     public QName createQName(String qualifiedName, String uri) {
    211         return cache.get(qualifiedName, uri);
    212     }
    213
    214     /***
    215      * <p>
    216      * <code>createXPath</code> parses an XPath expression and creates a new
    217      * XPath <code>XPath</code> instance.
    218      * </p>
    219      *
    220      * @param xpathExpression
    221      *            is the XPath expression to create
    222      *
    223      * @return a new <code>XPath</code> instance
    224      *
    225      * @throws InvalidXPathException
    226      *             if the XPath expression is invalid
    227      */
    228     public XPath createXPath(String xpathExpression)
    229             throws InvalidXPathException {
    230         DefaultXPath xpath = new DefaultXPath(xpathExpression);
    231
    232         if (xpathNamespaceURIs != null) {
    233             xpath.setNamespaceURIs(xpathNamespaceURIs);
    234         }
    235
    236         return xpath;
    237     }
    238
    239     /***
    240      * <p>
    241      * <code>createXPath</code> parses an XPath expression and creates a new
    242      * XPath <code>XPath</code> instance.
    243      * </p>
    244      *
    245      * @param xpathExpression
    246      *            is the XPath expression to create
    247      * @param variableContext
    248      *            is the variable context to use when evaluating the XPath
    249      *
    250      * @return a new <code>XPath</code> instance
    251      */
    252     public XPath createXPath(String xpathExpression,
    253             VariableContext variableContext) {
    254         XPath xpath = createXPath(xpathExpression);
    255         xpath.setVariableContext(variableContext);
    256
    257         return xpath;
    258     }
    259
    260     /***
    261      * <p>
    262      * <code>createXPathFilter</code> parses a NodeFilter from the given XPath
    263      * filter expression. XPath filter expressions occur within XPath
    264      * expressions such as <code>self::node()[ filterExpression ]</code>
    265      * </p>
    266      *
    267      * @param xpathFilterExpression
    268      *            is the XPath filter expression to create
    269      * @param variableContext
    270      *            is the variable context to use when evaluating the XPath
    271      *
    272      * @return a new <code>NodeFilter</code> instance
    273      */
    274     public NodeFilter createXPathFilter(String xpathFilterExpression,
    275             VariableContext variableContext) {
    276         XPath answer = createXPath(xpathFilterExpression);
    277
    278         // DefaultXPath answer = new DefaultXPath( xpathFilterExpression );
    279         answer.setVariableContext(variableContext);
    280
    281         return answer;
    282     }
    283
    284     /***
    285      * <p>
    286      * <code>createXPathFilter</code> parses a NodeFilter from the given XPath
    287      * filter expression. XPath filter expressions occur within XPath
    288      * expressions such as <code>self::node()[ filterExpression ]</code>
    289      * </p>
    290      *
    291      * @param xpathFilterExpression
    292      *            is the XPath filter expression to create
    293      *
    294      * @return a new <code>NodeFilter</code> instance
    295      */
    296     public NodeFilter createXPathFilter(String xpathFilterExpression) {
    297         return createXPath(xpathFilterExpression);
    298
    299         // return new DefaultXPath( xpathFilterExpression );
    300     }
    301
    302     /***
    303      * <p>
    304      * <code>createPattern</code> parses the given XPath expression to create
    305      * an XSLT style {@link Pattern}instance which can then be used in an XSLT
    306      * processing model.
    307      * </p>
    308      *
    309      * @param xpathPattern
    310      *            is the XPath pattern expression to create
    311      *
    312      * @return a new <code>Pattern</code> instance
    313      */
    314     public Pattern createPattern(String xpathPattern) {
    315         return new XPathPattern(xpathPattern);
    316     }
    317
    318     // Properties
    319     // -------------------------------------------------------------------------
    320
    321     /***
    322      * Returns a list of all the QName instances currently used by this document
    323      * factory
    324      *
    325      * @return DOCUMENT ME!
    326      */
    327     public List getQNames() {
    328         return cache.getQNames();
    329     }
    330
    331     /***
    332      * DOCUMENT ME!
    333      *
    334      * @return the Map of namespace URIs that will be used by by XPath
    335      *         expressions to resolve namespace prefixes into namespace URIs.
    336      *         The map is keyed by namespace prefix and the value is the
    337      *         namespace URI. This value could well be null to indicate no
    338      *         namespace URIs are being mapped.
    339      */
    340     public Map getXPathNamespaceURIs() {
    341         return xpathNamespaceURIs;
    342     }
    343
    344     /***
    345      * Sets the namespace URIs to be used by XPath expressions created by this
    346      * factory or by nodes associated with this factory. The keys are namespace
    347      * prefixes and the values are namespace URIs.
    348      *
    349      * @param namespaceURIs
    350      *            DOCUMENT ME!
    351      */
    352     public void setXPathNamespaceURIs(Map namespaceURIs) {
    353         this.xpathNamespaceURIs = namespaceURIs;
    354     }
    355
    356     // Implementation methods
    357     // -------------------------------------------------------------------------
    358
    359     /***
    360      * <p>
    361      * <code>createSingleton</code> creates the singleton instance from the
    362      * given class name.
    363      * </p>
    364      *
    365      * @param className
    366      *            is the name of the DocumentFactory class to use
    367      *
    368      * @return a new singleton instance.
    369      */
    370     protected static DocumentFactory createSingleton(String className) {
    371         // let's try and class load an implementation?
    372         try {
    373             // I'll use the current class loader
    374             // that loaded me to avoid problems in J2EE and web apps
    375             Class theClass = Class.forName(className, true,
    376                     DocumentFactory.class.getClassLoader());
    377
    378             return (DocumentFactory) theClass.newInstance();
    379         } catch (Throwable e) {
    380             System.out.println("WARNING: Cannot load DocumentFactory: "
    381                     + className);
    382
    383             return new DocumentFactory();
    384         }
    385     }
    386
    387     /***
    388      * DOCUMENT ME!
    389      *
    390      * @param qname
    391      *            DOCUMENT ME!
    392      *
    393      * @return the cached QName instance if there is one or adds the given qname
    394      *         to the cache if not
    395      */
    396     protected QName intern(QName qname) {
    397         return cache.intern(qname);
    398     }
    399
    400     /***
    401      * Factory method to create the QNameCache. This method should be overloaded
    402      * if you wish to use your own derivation of QName.
    403      *
    404      * @return DOCUMENT ME!
    405      */
    406     protected QNameCache createQNameCache() {
    407         return new QNameCache(this);
    408     }
    409
    410     private void readObject(ObjectInputStream in) throws IOException,
    411             ClassNotFoundException {
    412         in.defaultReadObject();
    413         init();
    414     }
    415
    416     protected void init() {
    417         cache = createQNameCache();
    418     }
    419 }
    420
    421 /*
    422  * Redistribution and use of this software and associated documentation
    423  * ("Software"), with or without modification, are permitted provided that the
    424  * following conditions are met:
    425  *
    426  * 1. Redistributions of source code must retain copyright statements and
    427  * notices. Redistributions must also contain a copy of this document.
    428  *
    429  * 2. Redistributions in binary form must reproduce the above copyright notice,
    430  * this list of conditions and the following disclaimer in the documentation
    431  * and/or other materials provided with the distribution.
    432  *
    433  * 3. The name "DOM4J" must not be used to endorse or promote products derived
    434  * from this Software without prior written permission of MetaStuff, Ltd. For
    435  * written permission, please contact dom4j-info@metastuff.com.
    436  *
    437  * 4. Products derived from this Software may not be called "DOM4J" nor may
    438  * "DOM4J" appear in their names without prior written permission of MetaStuff,
    439  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
    440  *
    441  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
    442  *
    443  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
    444  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    445  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    446  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
    447  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    448  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    449  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    450  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    451  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    452  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    453  * POSSIBILITY OF SUCH DAMAGE.
    454  *
    455  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    456  */