1
2
3
4
5
6
7
8
9
10 package org.dom4j;
11
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.io.Serializable;
15 import java.util.List;
16 import java.util.Map;
17
18 import org.dom4j.rule.Pattern;
19 import org.dom4j.tree.DefaultAttribute;
20 import org.dom4j.tree.DefaultCDATA;
21 import org.dom4j.tree.DefaultComment;
22 import org.dom4j.tree.DefaultDocument;
23 import org.dom4j.tree.DefaultDocumentType;
24 import org.dom4j.tree.DefaultElement;
25 import org.dom4j.tree.DefaultEntity;
26 import org.dom4j.tree.DefaultProcessingInstruction;
27 import org.dom4j.tree.DefaultText;
28 import org.dom4j.tree.QNameCache;
29 import org.dom4j.xpath.DefaultXPath;
30 import org.dom4j.xpath.XPathPattern;
31 import org.jaxen.VariableContext;
32
33 /*** <p><code>DocumentFactory</code> is a collection of factory methods to allow
34 * easy custom building of DOM4J trees. The default tree that is built uses
35 * a doubly linked tree. </p>
36 *
37 * <p>The tree built allows full XPath expressions from anywhere on the
38 * tree.</p>
39 *
40 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
41 * @version $Revision: 1.40 $
42 */
43 public class DocumentFactory implements Serializable {
44
45 /*** The Singleton instance */
46
47 private final static ThreadLocal singlePerThread=new ThreadLocal();
48 private static String documentFactoryClassName=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 static {
56 try {
57 documentFactoryClassName = System.getProperty(
58 "org.dom4j.factory",
59 "org.dom4j.DocumentFactory"
60 );
61 }
62 catch (Exception e) {
63 documentFactoryClassName = "org.dom4j.DocumentFactory";
64 }
65 getInstance();
66 }
67
68 /*** <p>Access to singleton implementation of DocumentFactory which
69 * is used if no DocumentFactory is specified when building using the
70 * standard builders.</p>
71 *
72 * @return the default singleon instance
73 */
74 public static DocumentFactory getInstance() {
75 DocumentFactory fact = (DocumentFactory)singlePerThread.get();
76 if (fact==null) {
77 fact=createSingleton( documentFactoryClassName );
78 singlePerThread.set(fact);
79 }
80 return fact;
81 }
82
83 public DocumentFactory() {
84 init();
85 }
86
87
88
89
90 public Document createDocument() {
91 DefaultDocument answer = new DefaultDocument();
92 answer.setDocumentFactory( this );
93 return answer;
94 }
95
96 /***
97 * @since 1.5
98 */
99 public Document createDocument(String encoding) {
100
101
102
103 Document answer = createDocument();
104 if (answer instanceof DefaultDocument) {
105 ((DefaultDocument) answer).setXMLEncoding(encoding);
106 }
107 return answer;
108 }
109
110 public Document createDocument(Element rootElement) {
111 Document answer = createDocument();
112 answer.setRootElement(rootElement);
113 return answer;
114 }
115
116 public DocumentType createDocType(String name, String publicId, String systemId) {
117 return new DefaultDocumentType( name, publicId, systemId );
118 }
119
120 public Element createElement(QName qname) {
121 return new DefaultElement(qname);
122 }
123
124 public Element createElement(String name) {
125 return createElement(createQName(name));
126 }
127
128 public Element createElement(String qualifiedName, String namespaceURI) {
129 return createElement(createQName(qualifiedName, namespaceURI));
130 }
131
132 public Attribute createAttribute(Element owner, QName qname, String value) {
133 return new DefaultAttribute(qname, value);
134 }
135
136 public Attribute createAttribute(Element owner, String name, String value) {
137 return createAttribute(owner, createQName(name), value);
138 }
139
140 public CDATA createCDATA(String text) {
141 return new DefaultCDATA(text);
142 }
143
144 public Comment createComment(String text) {
145 return new DefaultComment(text);
146 }
147
148 public Text createText(String text) {
149 if ( text == null ) {
150 throw new IllegalArgumentException( "Adding text to an XML document must not be null" );
151 }
152 return new DefaultText(text);
153 }
154
155
156 public Entity createEntity(String name, String text) {
157 return new DefaultEntity(name, text);
158 }
159
160 public Namespace createNamespace(String prefix, String uri) {
161 return Namespace.get(prefix, uri);
162 }
163
164 public ProcessingInstruction createProcessingInstruction(String target, String data) {
165 return new DefaultProcessingInstruction(target, data);
166 }
167
168 public ProcessingInstruction createProcessingInstruction(String target, Map data) {
169 return new DefaultProcessingInstruction(target, data);
170 }
171
172 public QName createQName(String localName, Namespace namespace) {
173 return cache.get(localName, namespace);
174 }
175
176 public QName createQName(String localName) {
177 return cache.get(localName);
178 }
179
180 public QName createQName(String name, String prefix, String uri) {
181 return cache.get(name, Namespace.get( prefix, uri ));
182 }
183
184 public QName createQName(String qualifiedName, String uri) {
185 return cache.get(qualifiedName, uri);
186 }
187
188 /*** <p><code>createXPath</code> parses an XPath expression
189 * and creates a new XPath <code>XPath</code> instance.</p>
190 *
191 * @param xpathExpression is the XPath expression to create
192 * @return a new <code>XPath</code> instance
193 * @throws InvalidXPathException if the XPath expression is invalid
194 */
195 public XPath createXPath(String xpathExpression) throws InvalidXPathException {
196 DefaultXPath xpath = new DefaultXPath( xpathExpression );
197 if ( xpathNamespaceURIs != null ) {
198 xpath.setNamespaceURIs( xpathNamespaceURIs );
199 }
200 return xpath;
201 }
202
203 /*** <p><code>createXPath</code> parses an XPath expression
204 * and creates a new XPath <code>XPath</code> instance.</p>
205 *
206 * @param xpathExpression is the XPath expression to create
207 * @param variableContext is the variable context to use when evaluating the XPath
208 * @return a new <code>XPath</code> instance
209 * @throws InvalidXPathException if the XPath expression is invalid
210 */
211 public XPath createXPath(String xpathExpression, VariableContext variableContext) {
212 XPath xpath = createXPath( xpathExpression );
213 xpath.setVariableContext( variableContext );
214 return xpath;
215 }
216
217 /*** <p><code>createXPathFilter</code> parses a NodeFilter
218 * from the given XPath filter expression.
219 * XPath filter expressions occur within XPath expressions such as
220 * <code>self::node()[ filterExpression ]</code></p>
221 *
222 * @param xpathFilterExpression is the XPath filter expression
223 * to create
224 * @param variableContext is the variable context to use when evaluating the XPath
225 * @return a new <code>NodeFilter</code> instance
226 */
227 public NodeFilter createXPathFilter(String xpathFilterExpression, VariableContext variableContext) {
228 XPath answer = createXPath( xpathFilterExpression );
229
230 answer.setVariableContext( variableContext );
231 return answer;
232 }
233
234 /*** <p><code>createXPathFilter</code> parses a NodeFilter
235 * from the given XPath filter expression.
236 * XPath filter expressions occur within XPath expressions such as
237 * <code>self::node()[ filterExpression ]</code></p>
238 *
239 * @param xpathFilterExpression is the XPath filter expression
240 * to create
241 * @return a new <code>NodeFilter</code> instance
242 */
243 public NodeFilter createXPathFilter(String xpathFilterExpression) {
244 return createXPath( xpathFilterExpression );
245
246 }
247
248 /*** <p><code>createPattern</code> parses the given
249 * XPath expression to create an XSLT style {@link Pattern} instance
250 * which can then be used in an XSLT processing model.</p>
251 *
252 * @param xpathPattern is the XPath pattern expression
253 * to create
254 * @return a new <code>Pattern</code> instance
255 */
256 public Pattern createPattern(String xpathPattern) {
257 return new XPathPattern( xpathPattern );
258 }
259
260
261
262
263
264 /*** Returns a list of all the QName instances currently used by this document factory
265 */
266 public List getQNames() {
267 return cache.getQNames();
268 }
269
270 /*** @return the Map of namespace URIs that will be used by by XPath expressions
271 * to resolve namespace prefixes into namespace URIs. The map is keyed by
272 * namespace prefix and the value is the namespace URI. This value could well be
273 * null to indicate no namespace URIs are being mapped.
274 */
275 public Map getXPathNamespaceURIs() {
276 return xpathNamespaceURIs;
277 }
278
279 /*** Sets the namespace URIs to be used by XPath expressions created by this factory
280 * or by nodes associated with this factory. The keys are namespace prefixes and the
281 * values are namespace URIs.
282 */
283 public void setXPathNamespaceURIs(Map xpathNamespaceURIs) {
284 this.xpathNamespaceURIs = xpathNamespaceURIs;
285 }
286
287
288
289
290
291 /*** <p><code>createSingleton</code> creates the singleton instance
292 * from the given class name.</p>
293 *
294 * @param className is the name of the DocumentFactory class to use
295 * @return a new singleton instance.
296 */
297 protected static DocumentFactory createSingleton(String className) {
298
299 try {
300
301
302 Class theClass = Class.forName(
303 className,
304 true,
305 DocumentFactory.class.getClassLoader()
306 );
307 return (DocumentFactory) theClass.newInstance();
308 }
309 catch (Throwable e) {
310 System.out.println( "WARNING: Cannot load DocumentFactory: " + className );
311 return new DocumentFactory();
312 }
313 }
314
315 /*** @return the cached QName instance if there is one or adds the given
316 * qname to the cache if not
317 */
318 protected QName intern(QName qname) {
319 return cache.intern(qname);
320 }
321
322 /*** Factory method to create the QNameCache. This method should be overloaded
323 * if you wish to use your own derivation of QName.
324 */
325 protected QNameCache createQNameCache() {
326 return new QNameCache(this);
327 }
328
329
330
331 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
332 in.defaultReadObject();
333 init();
334 }
335
336 protected void init() {
337 cache = createQNameCache();
338 }
339 }
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387