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%
coverage coverage
 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    */