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