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