Clover coverage report - dom4j - 1.6.1
Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00

file stats: | LOC: | 1.042 |  | Methods: | 58
| NCLOC: | 382 |  | Classes: | 2

Source file | Conditionals | Statements | Methods | TOTAL

SAXReader.java

51,9%

62,6%

58,6%

59,7%

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.io;
9 |  |
10 |  | import java.io.File;
11 |  | import java.io.FileInputStream;
12 |  | import java.io.FileNotFoundException;
13 |  | import java.io.InputStream;
14 |  | import java.io.Reader;
15 |  | import java.io.Serializable;
16 |  | import java.net.URL;
17 |  |
18 |  | import org.dom4j.Document;
19 |  | import org.dom4j.DocumentException;
20 |  | import org.dom4j.DocumentFactory;
21 |  | import org.dom4j.ElementHandler;
22 |  |
23 |  | import org.xml.sax.EntityResolver;
24 |  | import org.xml.sax.ErrorHandler;
25 |  | import org.xml.sax.InputSource;
26 |  | import org.xml.sax.SAXException;
27 |  | import org.xml.sax.SAXParseException;
28 |  | import org.xml.sax.XMLFilter;
29 |  | import org.xml.sax.XMLReader;
30 |  | import org.xml.sax.helpers.DefaultHandler;
31 |  | import org.xml.sax.helpers.XMLReaderFactory;
32 |  |
33 |  | /**
34 |  | * <p>
35 |  | * <code>SAXReader</code> creates a DOM4J tree from SAX parsing events.
36 |  | * </p>
37 |  | *
38 |  | * <p>
39 |  | * The actual SAX parser that is used by this class is configurable so you can
40 |  | * use your favourite SAX parser if you wish. DOM4J comes configured with its
41 |  | * own SAX parser so you do not need to worry about configuring the SAX parser.
42 |  | * </p>
43 |  | *
44 |  | * <p>
45 |  | * To explicitly configure the SAX parser that is used via Java code you can use
46 |  | * a constructor or use the {@link #setXMLReader(XMLReader)}or {@link
47 |  | * #setXMLReaderClassName(String)} methods.
48 |  | * </p>
49 |  | *
50 |  | * <p>
51 |  | * If the parser is not specified explicitly then the standard SAX policy of
52 |  | * using the <code>org.xml.sax.driver</code> system property is used to
53 |  | * determine the implementation class of {@link XMLReader}.
54 |  | * </p>
55 |  | *
56 |  | * <p>
57 |  | * If the <code>org.xml.sax.driver</code> system property is not defined then
58 |  | * JAXP is used via reflection (so that DOM4J is not explicitly dependent on the
59 |  | * JAXP classes) to load the JAXP configured SAXParser. If there is any error
60 |  | * creating a JAXP SAXParser an informational message is output and then the
61 |  | * default (Aelfred) SAX parser is used instead.
62 |  | * </p>
63 |  | *
64 |  | * <p>
65 |  | * If you are trying to use JAXP to explicitly set your SAX parser and are
66 |  | * experiencing problems, you can turn on verbose error reporting by defining
67 |  | * the system property <code>org.dom4j.verbose</code> to be "true" which will
68 |  | * output a more detailed description of why JAXP could not find a SAX parser
69 |  | * </p>
70 |  | *
71 |  | * <p>
72 |  | * For more information on JAXP please go to <a
73 |  | * href="http://java.sun.com/xml/">Sun's Java &amp; XML site </a>
74 |  | * </p>
75 |  | *
76 |  | * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
77 |  | * @version $Revision: 1.58 $
78 |  | */
79 |  | public class SAXReader {
80 |  | private static final String SAX_STRING_INTERNING =
81 |  | "http://xml.org/sax/features/string-interning";
82 |  | private static final String SAX_NAMESPACE_PREFIXES =
83 |  | "http://xml.org/sax/features/namespace-prefixes";
84 |  | private static final String SAX_NAMESPACES =
85 |  | "http://xml.org/sax/features/namespaces";
86 |  | private static final String SAX_DECL_HANDLER =
87 |  | "http://xml.org/sax/properties/declaration-handler";
88 |  | private static final String SAX_LEXICAL_HANDLER =
89 |  | "http://xml.org/sax/properties/lexical-handler";
90 |  | private static final String SAX_LEXICALHANDLER =
91 |  | "http://xml.org/sax/handlers/LexicalHandler";
92 |  |
93 |  | /** <code>DocumentFactory</code> used to create new document objects */
94 |  | private DocumentFactory factory;
95 |  |
96 |  | /** <code>XMLReader</code> used to parse the SAX events */
97 |  | private XMLReader xmlReader;
98 |  |
99 |  | /** Whether validation should occur */
100 |  | private boolean validating;
101 |  |
102 |  | /** DispatchHandler to call when each <code>Element</code> is encountered */
103 |  | private DispatchHandler dispatchHandler;
104 |  |
105 |  | /** ErrorHandler class to use */
106 |  | private ErrorHandler errorHandler;
107 |  |
108 |  | /** The entity resolver */
109 |  | private EntityResolver entityResolver;
110 |  |
111 |  | /** Should element & attribute names and namespace URIs be interned? */
112 |  | private boolean stringInternEnabled = true;
113 |  |
114 |  | /** Should internal DTD declarations be expanded into a List in the DTD */
115 |  | private boolean includeInternalDTDDeclarations = false;
116 |  |
117 |  | /** Should external DTD declarations be expanded into a List in the DTD */
118 |  | private boolean includeExternalDTDDeclarations = false;
119 |  |
120 |  | /** Whether adjacent text nodes should be merged */
121 |  | private boolean mergeAdjacentText = false;
122 |  |
123 |  | /** Holds value of property stripWhitespaceText. */
124 |  | private boolean stripWhitespaceText = false;
125 |  |
126 |  | /** Should we ignore comments */
127 |  | private boolean ignoreComments = false;
128 |  |
129 |  | /** Encoding of InputSource - null means system default encoding */
130 |  | private String encoding = null;
131 |  |
132 |  | // private boolean includeExternalGeneralEntities = false;
133 |  | // private boolean includeExternalParameterEntities = false;
134 |  |
135 |  | /** The SAX filter used to filter SAX events */
136 |  | private XMLFilter xmlFilter;
137 |  |
138 | 5741 | public SAXReader() {
139 |  | }
140 |  |
141 | 1 | public SAXReader(boolean validating) {
142 | 1 | this.validating = validating;
143 |  | }
144 |  |
145 | 55 | public SAXReader(DocumentFactory factory) {
146 | 55 | this.factory = factory;
147 |  | }
148 |  |
149 | 0 | public SAXReader(DocumentFactory factory, boolean validating) {
150 | 0 | this.factory = factory;
151 | 0 | this.validating = validating;
152 |  | }
153 |  |
154 | 0 | public SAXReader(XMLReader xmlReader) {
155 | 0 | this.xmlReader = xmlReader;
156 |  | }
157 |  |
158 | 0 | public SAXReader(XMLReader xmlReader, boolean validating) {
159 | 0 | this.xmlReader = xmlReader;
160 | 0 | this.validating = validating;
161 |  | }
162 |  |
163 | 1 | public SAXReader(String xmlReaderClassName) throws SAXException {
164 | 1 | if (xmlReaderClassName != null) {
165 | 1 | this.xmlReader = XMLReaderFactory
166 |  | .createXMLReader(xmlReaderClassName);
167 |  | }
168 |  | }
169 |  |
170 | 0 | public SAXReader(String xmlReaderClassName, boolean validating)
171 |  | throws SAXException {
172 | 0 | if (xmlReaderClassName != null) {
173 | 0 | this.xmlReader = XMLReaderFactory
174 |  | .createXMLReader(xmlReaderClassName);
175 |  | }
176 |  |
177 | 0 | this.validating = validating;
178 |  | }
179 |  |
180 |  | /**
181 |  | * Allows a SAX property to be set on the underlying SAX parser. This can be
182 |  | * useful to set parser-specific properties such as the location of schema
183 |  | * or DTD resources. Though use this method with caution as it has the
184 |  | * possibility of breaking the standard behaviour. An alternative to calling
185 |  | * this method is to correctly configure an XMLReader object instance and
186 |  | * call the {@link #setXMLReader(XMLReader)}method
187 |  | *
188 |  | * @param name
189 |  | * is the SAX property name
190 |  | * @param value
191 |  | * is the value of the SAX property
192 |  | *
193 |  | * @throws SAXException
194 |  | * if the XMLReader could not be created or the property could
195 |  | * not be changed.
196 |  | */
197 | 0 | public void setProperty(String name, Object value) throws SAXException {
198 | 0 | getXMLReader().setProperty(name, value);
199 |  | }
200 |  |
201 |  | /**
202 |  | * Sets a SAX feature on the underlying SAX parser. This can be useful to
203 |  | * set parser-specific features. Though use this method with caution as it
204 |  | * has the possibility of breaking the standard behaviour. An alternative to
205 |  | * calling this method is to correctly configure an XMLReader object
206 |  | * instance and call the {@link #setXMLReader(XMLReader)}method
207 |  | *
208 |  | * @param name
209 |  | * is the SAX feature name
210 |  | * @param value
211 |  | * is the value of the SAX feature
212 |  | *
213 |  | * @throws SAXException
214 |  | * if the XMLReader could not be created or the feature could
215 |  | * not be changed.
216 |  | */
217 | 0 | public void setFeature(String name, boolean value) throws SAXException {
218 | 0 | getXMLReader().setFeature(name, value);
219 |  | }
220 |  |
221 |  | /**
222 |  | * <p>
223 |  | * Reads a Document from the given <code>File</code>
224 |  | * </p>
225 |  | *
226 |  | * @param file
227 |  | * is the <code>File</code> to read from.
228 |  | *
229 |  | * @return the newly created Document instance
230 |  | *
231 |  | * @throws DocumentException
232 |  | * if an error occurs during parsing.
233 |  | */
234 | 204 | public Document read(File file) throws DocumentException {
235 | 204 | try {
236 |  | /*
237 |  | * We cannot convert the file to an URL because if the filename
238 |  | * contains '#' characters, there will be problems with the URL in
239 |  | * the InputSource (because a URL like
240 |  | * http://myhost.com/index#anchor is treated the same as
241 |  | * http://myhost.com/index) Thanks to Christian Oetterli
242 |  | */
243 | 204 | InputSource source = new InputSource(new FileInputStream(file));
244 | 204 | if (this.encoding != null) {
245 | 0 | source.setEncoding(this.encoding);
246 |  | }
247 | 204 | String path = file.getAbsolutePath();
248 |  |
249 | 204 | if (path != null) {
250 |  | // Code taken from Ant FileUtils
251 | 204 | StringBuffer sb = new StringBuffer("file://");
252 |  |
253 |  | // add an extra slash for filesystems with drive-specifiers
254 | 204 | if (!path.startsWith(File.separator)) {
255 | 204 | sb.append("/");
256 |  | }
257 |  |
258 | 204 | path = path.replace('\\', '/');
259 | 204 | sb.append(path);
260 |  |
261 | 204 | source.setSystemId(sb.toString());
262 |  | }
263 |  |
264 | 204 | return read(source);
265 |  | } catch (FileNotFoundException e) {
266 | 0 | throw new DocumentException(e.getMessage(), e);
267 |  | }
268 |  | }
269 |  |
270 |  | /**
271 |  | * <p>
272 |  | * Reads a Document from the given <code>URL</code> using SAX
273 |  | * </p>
274 |  | *
275 |  | * @param url
276 |  | * <code>URL</code> to read from.
277 |  | *
278 |  | * @return the newly created Document instance
279 |  | *
280 |  | * @throws DocumentException
281 |  | * if an error occurs during parsing.
282 |  | */
283 | 0 | public Document read(URL url) throws DocumentException {
284 | 0 | String systemID = url.toExternalForm();
285 |  |
286 | 0 | InputSource source = new InputSource(systemID);
287 | 0 | if (this.encoding != null) {
288 | 0 | source.setEncoding(this.encoding);
289 |  | }
290 |  |
291 | 0 | return read(source);
292 |  | }
293 |  |
294 |  | /**
295 |  | * <p>
296 |  | * Reads a Document from the given URL or filename using SAX.
297 |  | * </p>
298 |  | *
299 |  | * <p>
300 |  | * If the systemId contains a <code>':'</code> character then it is
301 |  | * assumed to be a URL otherwise its assumed to be a file name. If you want
302 |  | * finer grained control over this mechansim then please explicitly pass in
303 |  | * either a {@link URL}or a {@link File}instance instead of a {@link
304 |  | * String} to denote the source of the document.
305 |  | * </p>
306 |  | *
307 |  | * @param systemId
308 |  | * is a URL for a document or a file name.
309 |  | *
310 |  | * @return the newly created Document instance
311 |  | *
312 |  | * @throws DocumentException
313 |  | * if an error occurs during parsing.
314 |  | */
315 | 1 | public Document read(String systemId) throws DocumentException {
316 | 1 | InputSource source = new InputSource(systemId);
317 | 1 | if (this.encoding != null) {
318 | 0 | source.setEncoding(this.encoding);
319 |  | }
320 |  |
321 | 1 | return read(source);
322 |  | }
323 |  |
324 |  | /**
325 |  | * <p>
326 |  | * Reads a Document from the given stream using SAX
327 |  | * </p>
328 |  | *
329 |  | * @param in
330 |  | * <code>InputStream</code> to read from.
331 |  | *
332 |  | * @return the newly created Document instance
333 |  | *
334 |  | * @throws DocumentException
335 |  | * if an error occurs during parsing.
336 |  | */
337 | 1 | public Document read(InputStream in) throws DocumentException {
338 | 1 | InputSource source = new InputSource(in);
339 | 1 | if (this.encoding != null) {
340 | 0 | source.setEncoding(this.encoding);
341 |  | }
342 |  |
343 | 1 | return read(source);
344 |  | }
345 |  |
346 |  | /**
347 |  | * <p>
348 |  | * Reads a Document from the given <code>Reader</code> using SAX
349 |  | * </p>
350 |  | *
351 |  | * @param reader
352 |  | * is the reader for the input
353 |  | *
354 |  | * @return the newly created Document instance
355 |  | *
356 |  | * @throws DocumentException
357 |  | * if an error occurs during parsing.
358 |  | */
359 | 31 | public Document read(Reader reader) throws DocumentException {
360 | 31 | InputSource source = new InputSource(reader);
361 | 31 | if (this.encoding != null) {
362 | 1 | source.setEncoding(this.encoding);
363 |  | }
364 |  |
365 | 31 | return read(source);
366 |  | }
367 |  |
368 |  | /**
369 |  | * <p>
370 |  | * Reads a Document from the given stream using SAX
371 |  | * </p>
372 |  | *
373 |  | * @param in
374 |  | * <code>InputStream</code> to read from.
375 |  | * @param systemId
376 |  | * is the URI for the input
377 |  | *
378 |  | * @return the newly created Document instance
379 |  | *
380 |  | * @throws DocumentException
381 |  | * if an error occurs during parsing.
382 |  | */
383 | 0 | public Document read(InputStream in, String systemId)
384 |  | throws DocumentException {
385 | 0 | InputSource source = new InputSource(in);
386 | 0 | source.setSystemId(systemId);
387 | 0 | if (this.encoding != null) {
388 | 0 | source.setEncoding(this.encoding);
389 |  | }
390 |  |
391 | 0 | return read(source);
392 |  | }
393 |  |
394 |  | /**
395 |  | * <p>
396 |  | * Reads a Document from the given <code>Reader</code> using SAX
397 |  | * </p>
398 |  | *
399 |  | * @param reader
400 |  | * is the reader for the input
401 |  | * @param systemId
402 |  | * is the URI for the input
403 |  | *
404 |  | * @return the newly created Document instance
405 |  | *
406 |  | * @throws DocumentException
407 |  | * if an error occurs during parsing.
408 |  | */
409 | 0 | public Document read(Reader reader, String systemId)
410 |  | throws DocumentException {
411 | 0 | InputSource source = new InputSource(reader);
412 | 0 | source.setSystemId(systemId);
413 | 0 | if (this.encoding != null) {
414 | 0 | source.setEncoding(this.encoding);
415 |  | }
416 |  |
417 | 0 | return read(source);
418 |  | }
419 |  |
420 |  | /**
421 |  | * <p>
422 |  | * Reads a Document from the given <code>InputSource</code> using SAX
423 |  | * </p>
424 |  | *
425 |  | * @param in
426 |  | * <code>InputSource</code> to read from.
427 |  | *
428 |  | * @return the newly created Document instance
429 |  | *
430 |  | * @throws DocumentException
431 |  | * if an error occurs during parsing.
432 |  | */
433 | 5792 | public Document read(InputSource in) throws DocumentException {
434 | 5792 | try {
435 | 5792 | XMLReader reader = getXMLReader();
436 |  |
437 | 5792 | reader = installXMLFilter(reader);
438 |  |
439 | 5792 | EntityResolver thatEntityResolver = this.entityResolver;
440 |  |
441 | 5792 | if (thatEntityResolver == null) {
442 | 5762 | thatEntityResolver = createDefaultEntityResolver(in
443 |  | .getSystemId());
444 | 5762 | this.entityResolver = thatEntityResolver;
445 |  | }
446 |  |
447 | 5792 | reader.setEntityResolver(thatEntityResolver);
448 |  |
449 | 5792 | SAXContentHandler contentHandler = createContentHandler(reader);
450 | 5792 | contentHandler.setEntityResolver(thatEntityResolver);
451 | 5792 | contentHandler.setInputSource(in);
452 |  |
453 | 5792 | boolean internal = isIncludeInternalDTDDeclarations();
454 | 5792 | boolean external = isIncludeExternalDTDDeclarations();
455 |  |
456 | 5792 | contentHandler.setIncludeInternalDTDDeclarations(internal);
457 | 5792 | contentHandler.setIncludeExternalDTDDeclarations(external);
458 | 5792 | contentHandler.setMergeAdjacentText(isMergeAdjacentText());
459 | 5792 | contentHandler.setStripWhitespaceText(isStripWhitespaceText());
460 | 5792 | contentHandler.setIgnoreComments(isIgnoreComments());
461 | 5792 | reader.setContentHandler(contentHandler);
462 |  |
463 | 5792 | configureReader(reader, contentHandler);
464 |  |
465 | 5792 | reader.parse(in);
466 |  |
467 | 5791 | return contentHandler.getDocument();
468 |  | } catch (Exception e) {
469 | 1 | if (e instanceof SAXParseException) {
470 |  | // e.printStackTrace();
471 | 0 | SAXParseException parseException = (SAXParseException) e;
472 | 0 | String systemId = parseException.getSystemId();
473 |  |
474 | 0 | if (systemId == null) {
475 | 0 | systemId = "";
476 |  | }
477 |  |
478 | 0 | String message = "Error on line "
479 |  | + parseException.getLineNumber() + " of document "
480 |  | + systemId + " : " + parseException.getMessage();
481 |  |
482 | 0 | throw new DocumentException(message, e);
483 |  | } else {
484 | 1 | throw new DocumentException(e.getMessage(), e);
485 |  | }
486 |  | }
487 |  | }
488 |  |
489 |  | // Properties
490 |  | // -------------------------------------------------------------------------
491 |  |
492 |  | /**
493 |  | * DOCUMENT ME!
494 |  | *
495 |  | * @return the validation mode, true if validating will be done otherwise
496 |  | * false.
497 |  | */
498 | 11556 | public boolean isValidating() {
499 | 11556 | return validating;
500 |  | }
501 |  |
502 |  | /**
503 |  | * Sets the validation mode.
504 |  | *
505 |  | * @param validation
506 |  | * indicates whether or not validation should occur.
507 |  | */
508 | 0 | public void setValidation(boolean validation) {
509 | 0 | this.validating = validation;
510 |  | }
511 |  |
512 |  | /**
513 |  | * DOCUMENT ME!
514 |  | *
515 |  | * @return whether internal DTD declarations should be expanded into the
516 |  | * DocumentType object or not.
517 |  | */
518 | 5792 | public boolean isIncludeInternalDTDDeclarations() {
519 | 5792 | return includeInternalDTDDeclarations;
520 |  | }
521 |  |
522 |  | /**
523 |  | * Sets whether internal DTD declarations should be expanded into the
524 |  | * DocumentType object or not.
525 |  | *
526 |  | * @param include
527 |  | * whether or not DTD declarations should be expanded and
528 |  | * included into the DocumentType object.
529 |  | */
530 | 5 | public void setIncludeInternalDTDDeclarations(boolean include) {
531 | 5 | this.includeInternalDTDDeclarations = include;
532 |  | }
533 |  |
534 |  | /**
535 |  | * DOCUMENT ME!
536 |  | *
537 |  | * @return whether external DTD declarations should be expanded into the
538 |  | * DocumentType object or not.
539 |  | */
540 | 5792 | public boolean isIncludeExternalDTDDeclarations() {
541 | 5792 | return includeExternalDTDDeclarations;
542 |  | }
543 |  |
544 |  | /**
545 |  | * Sets whether DTD external declarations should be expanded into the
546 |  | * DocumentType object or not.
547 |  | *
548 |  | * @param include
549 |  | * whether or not DTD declarations should be expanded and
550 |  | * included into the DocumentType object.
551 |  | */
552 | 4 | public void setIncludeExternalDTDDeclarations(boolean include) {
553 | 4 | this.includeExternalDTDDeclarations = include;
554 |  | }
555 |  |
556 |  | /**
557 |  | * Sets whether String interning is enabled or disabled for element &
558 |  | * attribute names and namespace URIs. This proprety is enabled by default.
559 |  | *
560 |  | * @return DOCUMENT ME!
561 |  | */
562 | 5792 | public boolean isStringInternEnabled() {
563 | 5792 | return stringInternEnabled;
564 |  | }
565 |  |
566 |  | /**
567 |  | * Sets whether String interning is enabled or disabled for element &
568 |  | * attribute names and namespace URIs
569 |  | *
570 |  | * @param stringInternEnabled
571 |  | * DOCUMENT ME!
572 |  | */
573 | 0 | public void setStringInternEnabled(boolean stringInternEnabled) {
574 | 0 | this.stringInternEnabled = stringInternEnabled;
575 |  | }
576 |  |
577 |  | /**
578 |  | * Returns whether adjacent text nodes should be merged together.
579 |  | *
580 |  | * @return Value of property mergeAdjacentText.
581 |  | */
582 | 5792 | public boolean isMergeAdjacentText() {
583 | 5792 | return mergeAdjacentText;
584 |  | }
585 |  |
586 |  | /**
587 |  | * Sets whether or not adjacent text nodes should be merged together when
588 |  | * parsing.
589 |  | *
590 |  | * @param mergeAdjacentText
591 |  | * New value of property mergeAdjacentText.
592 |  | */
593 | 7 | public void setMergeAdjacentText(boolean mergeAdjacentText) {
594 | 7 | this.mergeAdjacentText = mergeAdjacentText;
595 |  | }
596 |  |
597 |  | /**
598 |  | * Sets whether whitespace between element start and end tags should be
599 |  | * ignored
600 |  | *
601 |  | * @return Value of property stripWhitespaceText.
602 |  | */
603 | 5792 | public boolean isStripWhitespaceText() {
604 | 5792 | return stripWhitespaceText;
605 |  | }
606 |  |
607 |  | /**
608 |  | * Sets whether whitespace between element start and end tags should be
609 |  | * ignored.
610 |  | *
611 |  | * @param stripWhitespaceText
612 |  | * New value of property stripWhitespaceText.
613 |  | */
614 | 0 | public void setStripWhitespaceText(boolean stripWhitespaceText) {
615 | 0 | this.stripWhitespaceText = stripWhitespaceText;
616 |  | }
617 |  |
618 |  | /**
619 |  | * Returns whether we should ignore comments or not.
620 |  | *
621 |  | * @return boolean
622 |  | */
623 | 5792 | public boolean isIgnoreComments() {
624 | 5792 | return ignoreComments;
625 |  | }
626 |  |
627 |  | /**
628 |  | * Sets whether we should ignore comments or not.
629 |  | *
630 |  | * @param ignoreComments
631 |  | * whether we should ignore comments or not.
632 |  | */
633 | 0 | public void setIgnoreComments(boolean ignoreComments) {
634 | 0 | this.ignoreComments = ignoreComments;
635 |  | }
636 |  |
637 |  | /**
638 |  | * DOCUMENT ME!
639 |  | *
640 |  | * @return the <code>DocumentFactory</code> used to create document
641 |  | * objects
642 |  | */
643 | 5792 | public DocumentFactory getDocumentFactory() {
644 | 5792 | if (factory == null) {
645 | 5702 | factory = DocumentFactory.getInstance();
646 |  | }
647 |  |
648 | 5792 | return factory;
649 |  | }
650 |  |
651 |  | /**
652 |  | * <p>
653 |  | * This sets the <code>DocumentFactory</code> used to create new
654 |  | * documents. This method allows the building of custom DOM4J tree objects
655 |  | * to be implemented easily using a custom derivation of
656 |  | * {@link DocumentFactory}
657 |  | * </p>
658 |  | *
659 |  | * @param documentFactory
660 |  | * <code>DocumentFactory</code> used to create DOM4J objects
661 |  | */
662 | 8 | public void setDocumentFactory(DocumentFactory documentFactory) {
663 | 8 | this.factory = documentFactory;
664 |  | }
665 |  |
666 |  | /**
667 |  | * DOCUMENT ME!
668 |  | *
669 |  | * @return the <code>ErrorHandler</code> used by SAX
670 |  | */
671 | 0 | public ErrorHandler getErrorHandler() {
672 | 0 | return errorHandler;
673 |  | }
674 |  |
675 |  | /**
676 |  | * Sets the <code>ErrorHandler</code> used by the SAX
677 |  | * <code>XMLReader</code>.
678 |  | *
679 |  | * @param errorHandler
680 |  | * is the <code>ErrorHandler</code> used by SAX
681 |  | */
682 | 0 | public void setErrorHandler(ErrorHandler errorHandler) {
683 | 0 | this.errorHandler = errorHandler;
684 |  | }
685 |  |
686 |  | /**
687 |  | * Returns the current entity resolver used to resolve entities
688 |  | *
689 |  | * @return DOCUMENT ME!
690 |  | */
691 | 0 | public EntityResolver getEntityResolver() {
692 | 0 | return entityResolver;
693 |  | }
694 |  |
695 |  | /**
696 |  | * Sets the entity resolver used to resolve entities.
697 |  | *
698 |  | * @param entityResolver
699 |  | * DOCUMENT ME!
700 |  | */
701 | 3 | public void setEntityResolver(EntityResolver entityResolver) {
702 | 3 | this.entityResolver = entityResolver;
703 |  | }
704 |  |
705 |  | /**
706 |  | * DOCUMENT ME!
707 |  | *
708 |  | * @return the <code>XMLReader</code> used to parse SAX events
709 |  | *
710 |  | * @throws SAXException
711 |  | * DOCUMENT ME!
712 |  | */
713 | 5792 | public XMLReader getXMLReader() throws SAXException {
714 | 5792 | if (xmlReader == null) {
715 | 5764 | xmlReader = createXMLReader();
716 |  | }
717 |  |
718 | 5792 | return xmlReader;
719 |  | }
720 |  |
721 |  | /**
722 |  | * Sets the <code>XMLReader</code> used to parse SAX events
723 |  | *
724 |  | * @param reader
725 |  | * is the <code>XMLReader</code> to parse SAX events
726 |  | */
727 | 0 | public void setXMLReader(XMLReader reader) {
728 | 0 | this.xmlReader = reader;
729 |  | }
730 |  |
731 |  | /**
732 |  | * Returns encoding used for InputSource (null means system default
733 |  | * encoding)
734 |  | *
735 |  | * @return encoding used for InputSource
736 |  | *
737 |  | */
738 | 0 | public String getEncoding() {
739 | 0 | return encoding;
740 |  | }
741 |  |
742 |  | /**
743 |  | * Sets encoding used for InputSource (null means system default encoding)
744 |  | *
745 |  | * @param encoding
746 |  | * is encoding used for InputSource
747 |  | */
748 | 1 | public void setEncoding(String encoding) {
749 | 1 | this.encoding = encoding;
750 |  | }
751 |  |
752 |  | /**
753 |  | * Sets the class name of the <code>XMLReader</code> to be used to parse
754 |  | * SAX events.
755 |  | *
756 |  | * @param xmlReaderClassName
757 |  | * is the class name of the <code>XMLReader</code> to parse SAX
758 |  | * events
759 |  | *
760 |  | * @throws SAXException
761 |  | * DOCUMENT ME!
762 |  | */
763 | 0 | public void setXMLReaderClassName(String xmlReaderClassName)
764 |  | throws SAXException {
765 | 0 | setXMLReader(XMLReaderFactory.createXMLReader(xmlReaderClassName));
766 |  | }
767 |  |
768 |  | /**
769 |  | * Adds the <code>ElementHandler</code> to be called when the specified
770 |  | * path is encounted.
771 |  | *
772 |  | * @param path
773 |  | * is the path to be handled
774 |  | * @param handler
775 |  | * is the <code>ElementHandler</code> to be called by the event
776 |  | * based processor.
777 |  | */
778 | 21 | public void addHandler(String path, ElementHandler handler) {
779 | 21 | getDispatchHandler().addHandler(path, handler);
780 |  | }
781 |  |
782 |  | /**
783 |  | * Removes the <code>ElementHandler</code> from the event based processor,
784 |  | * for the specified path.
785 |  | *
786 |  | * @param path
787 |  | * is the path to remove the <code>ElementHandler</code> for.
788 |  | */
789 | 0 | public void removeHandler(String path) {
790 | 0 | getDispatchHandler().removeHandler(path);
791 |  | }
792 |  |
793 |  | /**
794 |  | * When multiple <code>ElementHandler</code> instances have been
795 |  | * registered, this will set a default <code>ElementHandler</code> to be
796 |  | * called for any path which does <b>NOT </b> have a handler registered.
797 |  | *
798 |  | * @param handler
799 |  | * is the <code>ElementHandler</code> to be called by the event
800 |  | * based processor.
801 |  | */
802 | 0 | public void setDefaultHandler(ElementHandler handler) {
803 | 0 | getDispatchHandler().setDefaultHandler(handler);
804 |  | }
805 |  |
806 |  | /**
807 |  | * This method clears out all the existing handlers and default handler
808 |  | * setting things back as if no handler existed. Useful when reusing an
809 |  | * object instance.
810 |  | */
811 | 0 | public void resetHandlers() {
812 | 0 | getDispatchHandler().resetHandlers();
813 |  | }
814 |  |
815 |  | /**
816 |  | * Returns the SAX filter being used to filter SAX events.
817 |  | *
818 |  | * @return the SAX filter being used or null if no SAX filter is installed
819 |  | */
820 | 5792 | public XMLFilter getXMLFilter() {
821 | 5792 | return xmlFilter;
822 |  | }
823 |  |
824 |  | /**
825 |  | * Sets the SAX filter to be used when filtering SAX events
826 |  | *
827 |  | * @param filter
828 |  | * is the SAX filter to use or null to disable filtering
829 |  | */
830 | 0 | public void setXMLFilter(XMLFilter filter) {
831 | 0 | this.xmlFilter = filter;
832 |  | }
833 |  |
834 |  | // Implementation methods
835 |  | // -------------------------------------------------------------------------
836 |  |
837 |  | /**
838 |  | * Installs any XMLFilter objects required to allow the SAX event stream to
839 |  | * be filtered and preprocessed before it gets to dom4j.
840 |  | *
841 |  | * @param reader
842 |  | * DOCUMENT ME!
843 |  | *
844 |  | * @return the new XMLFilter if applicable or the original XMLReader if no
845 |  | * filter is being used.
846 |  | */
847 | 5792 | protected XMLReader installXMLFilter(XMLReader reader) {
848 | 5792 | XMLFilter filter = getXMLFilter();
849 |  |
850 | 5792 | if (filter != null) {
851 |  | // find the root XMLFilter
852 | 0 | XMLFilter root = filter;
853 |  |
854 | 0 | while (true) {
855 | 0 | XMLReader parent = root.getParent();
856 |  |
857 | 0 | if (parent instanceof XMLFilter) {
858 | 0 | root = (XMLFilter) parent;
859 |  | } else {
860 | 0 | break;
861 |  | }
862 |  | }
863 |  |
864 | 0 | root.setParent(reader);
865 |  |
866 | 0 | return filter;
867 |  | }
868 |  |
869 | 5792 | return reader;
870 |  | }
871 |  |
872 | 21 | protected DispatchHandler getDispatchHandler() {
873 | 21 | if (dispatchHandler == null) {
874 | 21 | dispatchHandler = new DispatchHandler();
875 |  | }
876 |  |
877 | 21 | return dispatchHandler;
878 |  | }
879 |  |
880 | 0 | protected void setDispatchHandler(DispatchHandler dispatchHandler) {
881 | 0 | this.dispatchHandler = dispatchHandler;
882 |  | }
883 |  |
884 |  | /**
885 |  | * Factory Method to allow alternate methods of creating and configuring
886 |  | * XMLReader objects
887 |  | *
888 |  | * @return DOCUMENT ME!
889 |  | *
890 |  | * @throws SAXException
891 |  | * DOCUMENT ME!
892 |  | */
893 | 5764 | protected XMLReader createXMLReader() throws SAXException {
894 | 5764 | return SAXHelper.createXMLReader(isValidating());
895 |  | }
896 |  |
897 |  | /**
898 |  | * Configures the XMLReader before use
899 |  | *
900 |  | * @param reader
901 |  | * DOCUMENT ME!
902 |  | * @param handler
903 |  | * DOCUMENT ME!
904 |  | *
905 |  | * @throws DocumentException
906 |  | * DOCUMENT ME!
907 |  | */
908 | 5792 | protected void configureReader(XMLReader reader, DefaultHandler handler)
909 |  | throws DocumentException {
910 |  | // configure lexical handling
911 | 5792 | SAXHelper.setParserProperty(reader, SAX_LEXICALHANDLER, handler);
912 |  |
913 |  | // try alternate property just in case
914 | 5792 | SAXHelper.setParserProperty(reader, SAX_LEXICAL_HANDLER, handler);
915 |  |
916 |  | // register the DeclHandler
917 | 5792 | if (includeInternalDTDDeclarations || includeExternalDTDDeclarations) {
918 | 6 | SAXHelper.setParserProperty(reader, SAX_DECL_HANDLER, handler);
919 |  | }
920 |  |
921 |  | // configure namespace support
922 | 5792 | SAXHelper.setParserFeature(reader, SAX_NAMESPACES, true);
923 |  |
924 | 5792 | SAXHelper.setParserFeature(reader, SAX_NAMESPACE_PREFIXES, false);
925 |  |
926 |  | // string interning
927 | 5792 | SAXHelper.setParserFeature(reader, SAX_STRING_INTERNING,
928 |  | isStringInternEnabled());
929 |  |
930 |  | // external entites
931 |  | /*
932 |  | * SAXHelper.setParserFeature( reader,
933 |  | * "http://xml.org/sax/properties/external-general-entities",
934 |  | * includeExternalGeneralEntities ); SAXHelper.setParserFeature( reader,
935 |  | * "http://xml.org/sax/properties/external-parameter-entities",
936 |  | * includeExternalParameterEntities );
937 |  | */
938 |  | // use Locator2 if possible
939 | 5792 | SAXHelper.setParserFeature(reader,
940 |  | "http://xml.org/sax/features/use-locator2", true);
941 |  |
942 | 5792 | try {
943 |  | // configure validation support
944 | 5792 | reader.setFeature("http://xml.org/sax/features/validation",
945 |  | isValidating());
946 |  |
947 | 5792 | if (errorHandler != null) {
948 | 0 | reader.setErrorHandler(errorHandler);
949 |  | } else {
950 | 5792 | reader.setErrorHandler(handler);
951 |  | }
952 |  | } catch (Exception e) {
953 | 0 | if (isValidating()) {
954 | 0 | throw new DocumentException("Validation not supported for"
955 |  | + " XMLReader: " + reader, e);
956 |  | }
957 |  | }
958 |  | }
959 |  |
960 |  | /**
961 |  | * Factory Method to allow user derived SAXContentHandler objects to be used
962 |  | *
963 |  | * @param reader
964 |  | * DOCUMENT ME!
965 |  | *
966 |  | * @return DOCUMENT ME!
967 |  | */
968 | 5792 | protected SAXContentHandler createContentHandler(XMLReader reader) {
969 | 5792 | return new SAXContentHandler(getDocumentFactory(), dispatchHandler);
970 |  | }
971 |  |
972 | 5762 | protected EntityResolver createDefaultEntityResolver(String systemId) {
973 | 5762 | String prefix = null;
974 |  |
975 | 5762 | if ((systemId != null) && (systemId.length() > 0)) {
976 | 179 | int idx = systemId.lastIndexOf('/');
977 |  |
978 | 179 | if (idx > 0) {
979 | 178 | prefix = systemId.substring(0, idx + 1);
980 |  | }
981 |  | }
982 |  |
983 | 5762 | return new SAXEntityResolver(prefix);
984 |  | }
985 |  |
986 |  | protected static class SAXEntityResolver implements EntityResolver,
987 |  | Serializable {
988 |  | protected String uriPrefix;
989 |  |
990 | 5762 | public SAXEntityResolver(String uriPrefix) {
991 | 5762 | this.uriPrefix = uriPrefix;
992 |  | }
993 |  |
994 | 5 | public InputSource resolveEntity(String publicId, String systemId) {
995 |  | // try create a relative URI reader...
996 | 5 | if ((systemId != null) && (systemId.length() > 0)) {
997 | 5 | if ((uriPrefix != null) && (systemId.indexOf(':') <= 0)) {
998 | 2 | systemId = uriPrefix + systemId;
999 |  | }
1000 |  | }
1001 |  |
1002 | 5 | return new InputSource(systemId);
1003 |  | }
1004 |  | }
1005 |  | }
1006 |  |
1007 |  | /*
1008 |  | * Redistribution and use of this software and associated documentation
1009 |  | * ("Software"), with or without modification, are permitted provided that the
1010 |  | * following conditions are met:
1011 |  | *
1012 |  | * 1. Redistributions of source code must retain copyright statements and
1013 |  | * notices. Redistributions must also contain a copy of this document.
1014 |  | *
1015 |  | * 2. Redistributions in binary form must reproduce the above copyright notice,
1016 |  | * this list of conditions and the following disclaimer in the documentation
1017 |  | * and/or other materials provided with the distribution.
1018 |  | *
1019 |  | * 3. The name "DOM4J" must not be used to endorse or promote products derived
1020 |  | * from this Software without prior written permission of MetaStuff, Ltd. For
1021 |  | * written permission, please contact dom4j-info@metastuff.com.
1022 |  | *
1023 |  | * 4. Products derived from this Software may not be called "DOM4J" nor may
1024 |  | * "DOM4J" appear in their names without prior written permission of MetaStuff,
1025 |  | * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
1026 |  | *
1027 |  | * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
1028 |  | *
1029 |  | * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
1030 |  | * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1031 |  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1032 |  | * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
1033 |  | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1034 |  | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1035 |  | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1036 |  | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1037 |  | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1038 |  | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1039 |  | * POSSIBILITY OF SUCH DAMAGE.
1040 |  | *
1041 |  | * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
1042 |  | */