1   
2   
3   
4   
5   
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     
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         
120         
121         
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         
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         
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     
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     
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         
372         try {
373             
374             
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 
423 
424 
425 
426 
427 
428 
429 
430 
431 
432 
433 
434 
435 
436 
437 
438 
439 
440 
441 
442 
443 
444 
445 
446 
447 
448 
449 
450 
451 
452 
453 
454 
455 
456