View Javadoc

    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.lang.reflect.Method;
    11  import java.util.ArrayList;
    12  import java.util.HashMap;
    13  import java.util.List;
    14  import java.util.Map;
    15
    16  import org.dom4j.Branch;
    17  import org.dom4j.Document;
    18  import org.dom4j.DocumentFactory;
    19  import org.dom4j.DocumentType;
    20  import org.dom4j.Element;
    21  import org.dom4j.ElementHandler;
    22  import org.dom4j.Namespace;
    23  import org.dom4j.QName;
    24  import org.dom4j.dtd.AttributeDecl;
    25  import org.dom4j.dtd.ElementDecl;
    26  import org.dom4j.dtd.ExternalEntityDecl;
    27  import org.dom4j.dtd.InternalEntityDecl;
    28  import org.dom4j.tree.AbstractElement;
    29  import org.dom4j.tree.NamespaceStack;
    30
    31  import org.xml.sax.Attributes;
    32  import org.xml.sax.DTDHandler;
    33  import org.xml.sax.EntityResolver;
    34  import org.xml.sax.InputSource;
    35  import org.xml.sax.Locator;
    36  import org.xml.sax.SAXException;
    37  import org.xml.sax.SAXParseException;
    38  import org.xml.sax.ext.DeclHandler;
    39  import org.xml.sax.ext.LexicalHandler;
    40  import org.xml.sax.helpers.DefaultHandler;
    41
    42  /***
    43   * <p>
    44   * <code>SAXContentHandler</code> builds a dom4j tree via SAX events.
    45   * </p>
    46   *
    47   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
    48   * @version $Revision: 1.61 $
    49   */
    50  public class SAXContentHandler extends DefaultHandler implements
    51          LexicalHandler, DeclHandler, DTDHandler {
    52      /*** The factory used to create new <code>Document</code> instances */
    53      private DocumentFactory documentFactory;
    54
    55      /*** The document that is being built */
    56      private Document document;
    57
    58      /*** stack of <code>Element</code> objects */
    59      private ElementStack elementStack;
    60
    61      /*** stack of <code>Namespace</code> and <code>QName</code> objects */
    62      private NamespaceStack namespaceStack;
    63
    64      /*** the <code>ElementHandler</code> called as the elements are complete */
    65      private ElementHandler elementHandler;
    66
    67      /*** the Locator */
    68      private Locator locator;
    69
    70      /*** The name of the current entity */
    71      private String entity;
    72
    73      /*** Flag used to indicate that we are inside a DTD section */
    74      private boolean insideDTDSection;
    75
    76      /*** Flag used to indicate that we are inside a CDATA section */
    77      private boolean insideCDATASection;
    78
    79      /***
    80       * buffer to hold contents of cdata section across multiple characters
    81       * events
    82       */
    83      private StringBuffer cdataText;
    84
    85      /*** namespaces that are available for use */
    86      private Map availableNamespaceMap = new HashMap();
    87
    88      /*** declared namespaces that are not yet available for use */
    89      private List declaredNamespaceList = new ArrayList();
    90
    91      /*** internal DTD declarations */
    92      private List internalDTDDeclarations;
    93
    94      /*** external DTD declarations */
    95      private List externalDTDDeclarations;
    96
    97      /*** The number of namespaces that are declared in the current scope */
    98      private int declaredNamespaceIndex;
    99
    100     /*** The entity resolver */
    101     private EntityResolver entityResolver;
    102
    103     private InputSource inputSource;
    104
    105     /*** The current element we are on */
    106     private Element currentElement;
    107
    108     /*** Should internal DTD declarations be expanded into a List in the DTD */
    109     private boolean includeInternalDTDDeclarations = false;
    110
    111     /*** Should external DTD declarations be expanded into a List in the DTD */
    112     private boolean includeExternalDTDDeclarations = false;
    113
    114     /*** The number of levels deep we are inside a startEntity/endEntity call */
    115     private int entityLevel;
    116
    117     /*** Are we in an internal DTD subset? */
    118     private boolean internalDTDsubset = false;
    119
    120     /*** Whether adjacent text nodes should be merged */
    121     private boolean mergeAdjacentText = false;
    122
    123     /*** Have we added text to the buffer */
    124     private boolean textInTextBuffer = false;
    125
    126     /*** Should we ignore comments */
    127     private boolean ignoreComments = false;
    128
    129     /*** Buffer used to concatenate text together */
    130     private StringBuffer textBuffer;
    131
    132     /*** Holds value of property stripWhitespaceText. */
    133     private boolean stripWhitespaceText = false;
    134
    135     public SAXContentHandler() {
    136         this(DocumentFactory.getInstance());
    137     }
    138
    139     public SAXContentHandler(DocumentFactory documentFactory) {
    140         this(documentFactory, null);
    141     }
    142
    143     public SAXContentHandler(DocumentFactory documentFactory,
    144             ElementHandler elementHandler) {
    145         this(documentFactory, elementHandler, null);
    146         this.elementStack = createElementStack();
    147     }
    148
    149     public SAXContentHandler(DocumentFactory documentFactory,
    150             ElementHandler elementHandler, ElementStack elementStack) {
    151         this.documentFactory = documentFactory;
    152         this.elementHandler = elementHandler;
    153         this.elementStack = elementStack;
    154         this.namespaceStack = new NamespaceStack(documentFactory);
    155     }
    156
    157     /***
    158      * DOCUMENT ME!
    159      *
    160      * @return the document that has been or is being built
    161      */
    162     public Document getDocument() {
    163         if (document == null) {
    164             document = createDocument();
    165         }
    166
    167         return document;
    168     }
    169
    170     // ContentHandler interface
    171     // -------------------------------------------------------------------------
    172     public void setDocumentLocator(Locator documentLocator) {
    173         this.locator = documentLocator;
    174     }
    175
    176     public void processingInstruction(String target, String data)
    177             throws SAXException {
    178         if (mergeAdjacentText && textInTextBuffer) {
    179             completeCurrentTextNode();
    180         }
    181
    182         if (currentElement != null) {
    183             currentElement.addProcessingInstruction(target, data);
    184         } else {
    185             getDocument().addProcessingInstruction(target, data);
    186         }
    187     }
    188
    189     public void startPrefixMapping(String prefix, String uri)
    190             throws SAXException {
    191         namespaceStack.push(prefix, uri);
    192     }
    193
    194     public void endPrefixMapping(String prefix) throws SAXException {
    195         namespaceStack.pop(prefix);
    196         declaredNamespaceIndex = namespaceStack.size();
    197     }
    198
    199     public void startDocument() throws SAXException {
    200         // document = createDocument();
    201         document = null;
    202         currentElement = null;
    203
    204         elementStack.clear();
    205
    206         if ((elementHandler != null)
    207                 && (elementHandler instanceof DispatchHandler)) {
    208             elementStack.setDispatchHandler((DispatchHandler) elementHandler);
    209         }
    210
    211         namespaceStack.clear();
    212         declaredNamespaceIndex = 0;
    213
    214         if (mergeAdjacentText && (textBuffer == null)) {
    215             textBuffer = new StringBuffer();
    216         }
    217
    218         textInTextBuffer = false;
    219     }
    220
    221     public void endDocument() throws SAXException {
    222         namespaceStack.clear();
    223         elementStack.clear();
    224         currentElement = null;
    225         textBuffer = null;
    226     }
    227
    228     public void startElement(String namespaceURI, String localName,
    229             String qualifiedName, Attributes attributes) throws SAXException {
    230         if (mergeAdjacentText && textInTextBuffer) {
    231             completeCurrentTextNode();
    232         }
    233
    234         QName qName = namespaceStack.getQName(namespaceURI, localName,
    235                 qualifiedName);
    236
    237         Branch branch = currentElement;
    238
    239         if (branch == null) {
    240             branch = getDocument();
    241         }
    242
    243         Element element = branch.addElement(qName);
    244
    245         // add all declared namespaces
    246         addDeclaredNamespaces(element);
    247
    248         // now lets add all attribute values
    249         addAttributes(element, attributes);
    250
    251         elementStack.pushElement(element);
    252         currentElement = element;
    253
    254         entity = null; // fixes bug527062
    255
    256         if (elementHandler != null) {
    257             elementHandler.onStart(elementStack);
    258         }
    259     }
    260
    261     public void endElement(String namespaceURI, String localName, String qName)
    262             throws SAXException {
    263         if (mergeAdjacentText && textInTextBuffer) {
    264             completeCurrentTextNode();
    265         }
    266
    267         if ((elementHandler != null) && (currentElement != null)) {
    268             elementHandler.onEnd(elementStack);
    269         }
    270
    271         elementStack.popElement();
    272         currentElement = elementStack.peekElement();
    273     }
    274
    275     public void characters(char[] ch, int start, int end) throws SAXException {
    276         if (end == 0) {
    277             return;
    278         }
    279
    280         if (currentElement != null) {
    281             if (entity != null) {
    282                 if (mergeAdjacentText && textInTextBuffer) {
    283                     completeCurrentTextNode();
    284                 }
    285
    286                 currentElement.addEntity(entity, new String(ch, start, end));
    287                 entity = null;
    288             } else if (insideCDATASection) {
    289                 if (mergeAdjacentText && textInTextBuffer) {
    290                     completeCurrentTextNode();
    291                 }
    292
    293                 cdataText.append(new String(ch, start, end));
    294             } else {
    295                 if (mergeAdjacentText) {
    296                     textBuffer.append(ch, start, end);
    297                     textInTextBuffer = true;
    298                 } else {
    299                     currentElement.addText(new String(ch, start, end));
    300                 }
    301             }
    302         }
    303     }
    304
    305     // ErrorHandler interface
    306     // -------------------------------------------------------------------------
    307
    308     /***
    309      * This method is called when a warning occurs during the parsing of the
    310      * document. This method does nothing.
    311      *
    312      * @param exception
    313      *            DOCUMENT ME!
    314      *
    315      * @throws SAXException
    316      *             DOCUMENT ME!
    317      */
    318     public void warning(SAXParseException exception) throws SAXException {
    319         // ignore warnings by default
    320     }
    321
    322     /***
    323      * This method is called when an error is detected during parsing such as a
    324      * validation error. This method rethrows the exception
    325      *
    326      * @param exception
    327      *            DOCUMENT ME!
    328      *
    329      * @throws SAXException
    330      *             DOCUMENT ME!
    331      */
    332     public void error(SAXParseException exception) throws SAXException {
    333         throw exception;
    334     }
    335
    336     /***
    337      * This method is called when a fatal error occurs during parsing. This
    338      * method rethrows the exception
    339      *
    340      * @param exception
    341      *            DOCUMENT ME!
    342      *
    343      * @throws SAXException
    344      *             DOCUMENT ME!
    345      */
    346     public void fatalError(SAXParseException exception) throws SAXException {
    347         throw exception;
    348     }
    349
    350     // LexicalHandler interface
    351     // -------------------------------------------------------------------------
    352     public void startDTD(String name, String publicId, String systemId)
    353             throws SAXException {
    354         getDocument().addDocType(name, publicId, systemId);
    355         insideDTDSection = true;
    356         internalDTDsubset = true;
    357     }
    358
    359     public void endDTD() throws SAXException {
    360         insideDTDSection = false;
    361
    362         DocumentType docType = getDocument().getDocType();
    363
    364         if (docType != null) {
    365             if (internalDTDDeclarations != null) {
    366                 docType.setInternalDeclarations(internalDTDDeclarations);
    367             }
    368
    369             if (externalDTDDeclarations != null) {
    370                 docType.setExternalDeclarations(externalDTDDeclarations);
    371             }
    372         }
    373
    374         internalDTDDeclarations = null;
    375         externalDTDDeclarations = null;
    376     }
    377
    378     public void startEntity(String name) throws SAXException {
    379         ++entityLevel;
    380
    381         // Ignore DTD references
    382         entity = null;
    383
    384         if (!insideDTDSection) {
    385             if (!isIgnorableEntity(name)) {
    386                 entity = name;
    387             }
    388         }
    389
    390         // internal DTD subsets can only appear outside of a
    391         // startEntity/endEntity block
    392         // see the startDTD method in
    393         // http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html
    394         internalDTDsubset = false;
    395     }
    396
    397     public void endEntity(String name) throws SAXException {
    398         --entityLevel;
    399         entity = null;
    400
    401         if (entityLevel == 0) {
    402             internalDTDsubset = true;
    403         }
    404     }
    405
    406     public void startCDATA() throws SAXException {
    407         insideCDATASection = true;
    408         cdataText = new StringBuffer();
    409     }
    410
    411     public void endCDATA() throws SAXException {
    412         insideCDATASection = false;
    413         currentElement.addCDATA(cdataText.toString());
    414     }
    415
    416     public void comment(char[] ch, int start, int end) throws SAXException {
    417         if (!ignoreComments) {
    418             if (mergeAdjacentText && textInTextBuffer) {
    419                 completeCurrentTextNode();
    420             }
    421
    422             String text = new String(ch, start, end);
    423
    424             if (!insideDTDSection && (text.length() > 0)) {
    425                 if (currentElement != null) {
    426                     currentElement.addComment(text);
    427                 } else {
    428                     getDocument().addComment(text);
    429                 }
    430             }
    431         }
    432     }
    433
    434     // DeclHandler interface
    435     // -------------------------------------------------------------------------
    436
    437     /***
    438      * Report an element type declaration.
    439      *
    440      * <p>
    441      * The content model will consist of the string "EMPTY", the string "ANY",
    442      * or a parenthesised group, optionally followed by an occurrence indicator.
    443      * The model will be normalized so that all parameter entities are fully
    444      * resolved and all whitespace is removed,and will include the enclosing
    445      * parentheses. Other normalization (such as removing redundant parentheses
    446      * or simplifying occurrence indicators) is at the discretion of the parser.
    447      * </p>
    448      *
    449      * @param name
    450      *            The element type name.
    451      * @param model
    452      *            The content model as a normalized string.
    453      *
    454      * @exception SAXException
    455      *                The application may raise an exception.
    456      */
    457     public void elementDecl(String name, String model) throws SAXException {
    458         if (internalDTDsubset) {
    459             if (includeInternalDTDDeclarations) {
    460                 addDTDDeclaration(new ElementDecl(name, model));
    461             }
    462         } else {
    463             if (includeExternalDTDDeclarations) {
    464                 addExternalDTDDeclaration(new ElementDecl(name, model));
    465             }
    466         }
    467     }
    468
    469     /***
    470      * Report an attribute type declaration.
    471      *
    472      * <p>
    473      * Only the effective (first) declaration for an attribute will be reported.
    474      * The type will be one of the strings "CDATA", "ID", "IDREF", "IDREFS",
    475      * "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", a parenthesized token group
    476      * with the separator "|" and all whitespace removed, or the word "NOTATION"
    477      * followed by a space followed by a parenthesized token group with all
    478      * whitespace removed.
    479      * </p>
    480      *
    481      * <p>
    482      * Any parameter entities in the attribute value will be expanded, but
    483      * general entities will not.
    484      * </p>
    485      *
    486      * @param eName
    487      *            The name of the associated element.
    488      * @param aName
    489      *            The name of the attribute.
    490      * @param type
    491      *            A string representing the attribute type.
    492      * @param valueDefault
    493      *            A string representing the attribute default ("#IMPLIED",
    494      *            "#REQUIRED", or "#FIXED") or null if none of these applies.
    495      * @param val
    496      *            A string representing the attribute's default value, or null
    497      *            if there is none.
    498      *
    499      * @exception SAXException
    500      *                The application may raise an exception.
    501      */
    502     public void attributeDecl(String eName, String aName, String type,
    503             String valueDefault, String val) throws SAXException {
    504         if (internalDTDsubset) {
    505             if (includeInternalDTDDeclarations) {
    506                 addDTDDeclaration(new AttributeDecl(eName, aName, type,
    507                         valueDefault, val));
    508             }
    509         } else {
    510             if (includeExternalDTDDeclarations) {
    511                 addExternalDTDDeclaration(new AttributeDecl(eName, aName, type,
    512                         valueDefault, val));
    513             }
    514         }
    515     }
    516
    517     /***
    518      * Report an internal entity declaration.
    519      *
    520      * <p>
    521      * Only the effective (first) declaration for each entity will be reported.
    522      * All parameter entities in the value will be expanded, but general
    523      * entities will not.
    524      * </p>
    525      *
    526      * @param name
    527      *            The name of the entity. If it is a parameter entity, the name
    528      *            will begin with '%'.
    529      * @param value
    530      *            The replacement text of the entity.
    531      *
    532      * @exception SAXException
    533      *                The application may raise an exception.
    534      *
    535      * @see #externalEntityDecl
    536      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
    537      */
    538     public void internalEntityDecl(String name, String value)
    539             throws SAXException {
    540         if (internalDTDsubset) {
    541             if (includeInternalDTDDeclarations) {
    542                 addDTDDeclaration(new InternalEntityDecl(name, value));
    543             }
    544         } else {
    545             if (includeExternalDTDDeclarations) {
    546                 addExternalDTDDeclaration(new InternalEntityDecl(name, value));
    547             }
    548         }
    549     }
    550
    551     /***
    552      * Report a parsed external entity declaration.
    553      *
    554      * <p>
    555      * Only the effective (first) declaration for each entity will be reported.
    556      * </p>
    557      *
    558      * @param name
    559      *            The name of the entity. If it is a parameter entity, the name
    560      *            will begin with '%'.
    561      * @param publicId
    562      *            The declared public identifier of the entity, or null if none
    563      *            was declared.
    564      * @param sysId
    565      *            The declared system identifier of the entity.
    566      *
    567      * @exception SAXException
    568      *                The application may raise an exception.
    569      *
    570      * @see #internalEntityDecl
    571      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
    572      */
    573     public void externalEntityDecl(String name, String publicId, String sysId)
    574             throws SAXException {
    575         ExternalEntityDecl declaration = new ExternalEntityDecl(name, publicId,
    576                 sysId);
    577
    578         if (internalDTDsubset) {
    579             if (includeInternalDTDDeclarations) {
    580                 addDTDDeclaration(declaration);
    581             }
    582         } else {
    583             if (includeExternalDTDDeclarations) {
    584                 addExternalDTDDeclaration(declaration);
    585             }
    586         }
    587     }
    588
    589     // DTDHandler interface
    590     // -------------------------------------------------------------------------
    591
    592     /***
    593      * Receive notification of a notation declaration event.
    594      *
    595      * <p>
    596      * It is up to the application to record the notation for later reference,
    597      * if necessary.
    598      * </p>
    599      *
    600      * <p>
    601      * At least one of publicId and systemId must be non-null. If a system
    602      * identifier is present, and it is a URL, the SAX parser must resolve it
    603      * fully before passing it to the application through this event.
    604      * </p>
    605      *
    606      * <p>
    607      * There is no guarantee that the notation declaration will be reported
    608      * before any unparsed entities that use it.
    609      * </p>
    610      *
    611      * @param name
    612      *            The notation name.
    613      * @param publicId
    614      *            The notation's public identifier, or null if none was given.
    615      * @param systemId
    616      *            The notation's system identifier, or null if none was given.
    617      *
    618      * @exception SAXException
    619      *                Any SAX exception, possibly wrapping another exception.
    620      *
    621      * @see #unparsedEntityDecl
    622      * @see org.xml.sax.AttributeList
    623      */
    624     public void notationDecl(String name, String publicId, String systemId)
    625             throws SAXException {
    626         // #### not supported yet!
    627     }
    628
    629     /***
    630      * Receive notification of an unparsed entity declaration event.
    631      *
    632      * <p>
    633      * Note that the notation name corresponds to a notation reported by the
    634      * {@link #notationDecl notationDecl}event. It is up to the application to
    635      * record the entity for later reference, if necessary.
    636      * </p>
    637      *
    638      * <p>
    639      * If the system identifier is a URL, the parser must resolve it fully
    640      * before passing it to the application.
    641      * </p>
    642      *
    643      * @param name
    644      *            The unparsed entity's name.
    645      * @param publicId
    646      *            The entity's public identifier, or null if none was given.
    647      * @param systemId
    648      *            The entity's system identifier.
    649      * @param notationName
    650      *            The name of the associated notation.
    651      *
    652      * @exception SAXException
    653      *                Any SAX exception, possibly wrapping another exception.
    654      *
    655      * @see #notationDecl
    656      * @see org.xml.sax.AttributeList
    657      */
    658     public void unparsedEntityDecl(String name, String publicId,
    659             String systemId, String notationName) throws SAXException {
    660         // #### not supported yet!
    661     }
    662
    663     // Properties
    664     // -------------------------------------------------------------------------
    665     public ElementStack getElementStack() {
    666         return elementStack;
    667     }
    668
    669     public void setElementStack(ElementStack elementStack) {
    670         this.elementStack = elementStack;
    671     }
    672
    673     public EntityResolver getEntityResolver() {
    674         return entityResolver;
    675     }
    676
    677     public void setEntityResolver(EntityResolver entityResolver) {
    678         this.entityResolver = entityResolver;
    679     }
    680
    681     public InputSource getInputSource() {
    682         return inputSource;
    683     }
    684
    685     public void setInputSource(InputSource inputSource) {
    686         this.inputSource = inputSource;
    687     }
    688
    689     /***
    690      * DOCUMENT ME!
    691      *
    692      * @return whether internal DTD declarations should be expanded into the
    693      *         DocumentType object or not.
    694      */
    695     public boolean isIncludeInternalDTDDeclarations() {
    696         return includeInternalDTDDeclarations;
    697     }
    698
    699     /***
    700      * Sets whether internal DTD declarations should be expanded into the
    701      * DocumentType object or not.
    702      *
    703      * @param include
    704      *            whether or not DTD declarations should be expanded and
    705      *            included into the DocumentType object.
    706      */
    707     public void setIncludeInternalDTDDeclarations(boolean include) {
    708         this.includeInternalDTDDeclarations = include;
    709     }
    710
    711     /***
    712      * DOCUMENT ME!
    713      *
    714      * @return whether external DTD declarations should be expanded into the
    715      *         DocumentType object or not.
    716      */
    717     public boolean isIncludeExternalDTDDeclarations() {
    718         return includeExternalDTDDeclarations;
    719     }
    720
    721     /***
    722      * Sets whether DTD external declarations should be expanded into the
    723      * DocumentType object or not.
    724      *
    725      * @param include
    726      *            whether or not DTD declarations should be expanded and
    727      *            included into the DocumentType object.
    728      */
    729     public void setIncludeExternalDTDDeclarations(boolean include) {
    730         this.includeExternalDTDDeclarations = include;
    731     }
    732
    733     /***
    734      * Returns whether adjacent text nodes should be merged together.
    735      *
    736      * @return Value of property mergeAdjacentText.
    737      */
    738     public boolean isMergeAdjacentText() {
    739         return mergeAdjacentText;
    740     }
    741
    742     /***
    743      * Sets whether or not adjacent text nodes should be merged together when
    744      * parsing.
    745      *
    746      * @param mergeAdjacentText
    747      *            New value of property mergeAdjacentText.
    748      */
    749     public void setMergeAdjacentText(boolean mergeAdjacentText) {
    750         this.mergeAdjacentText = mergeAdjacentText;
    751     }
    752
    753     /***
    754      * Sets whether whitespace between element start and end tags should be
    755      * ignored
    756      *
    757      * @return Value of property stripWhitespaceText.
    758      */
    759     public boolean isStripWhitespaceText() {
    760         return stripWhitespaceText;
    761     }
    762
    763     /***
    764      * Sets whether whitespace between element start and end tags should be
    765      * ignored.
    766      *
    767      * @param stripWhitespaceText
    768      *            New value of property stripWhitespaceText.
    769      */
    770     public void setStripWhitespaceText(boolean stripWhitespaceText) {
    771         this.stripWhitespaceText = stripWhitespaceText;
    772     }
    773
    774     /***
    775      * Returns whether we should ignore comments or not.
    776      *
    777      * @return boolean
    778      */
    779     public boolean isIgnoreComments() {
    780         return ignoreComments;
    781     }
    782
    783     /***
    784      * Sets whether we should ignore comments or not.
    785      *
    786      * @param ignoreComments
    787      *            whether we should ignore comments or not.
    788      */
    789     public void setIgnoreComments(boolean ignoreComments) {
    790         this.ignoreComments = ignoreComments;
    791     }
    792
    793     // Implementation methods
    794     // -------------------------------------------------------------------------
    795
    796     /***
    797      * If the current text buffer contains any text then create a new text node
    798      * with it and add it to the current element
    799      */
    800     protected void completeCurrentTextNode() {
    801         if (stripWhitespaceText) {
    802             boolean whitespace = true;
    803
    804             for (int i = 0, size = textBuffer.length(); i < size; i++) {
    805                 if (!Character.isWhitespace(textBuffer.charAt(i))) {
    806                     whitespace = false;
    807
    808                     break;
    809                 }
    810             }
    811
    812             if (!whitespace) {
    813                 currentElement.addText(textBuffer.toString());
    814             }
    815         } else {
    816             currentElement.addText(textBuffer.toString());
    817         }
    818
    819         textBuffer.setLength(0);
    820         textInTextBuffer = false;
    821     }
    822
    823     /***
    824      * DOCUMENT ME!
    825      *
    826      * @return the current document
    827      */
    828     protected Document createDocument() {
    829         String encoding = getEncoding();
    830         Document doc = documentFactory.createDocument(encoding);
    831
    832         // set the EntityResolver
    833         doc.setEntityResolver(entityResolver);
    834
    835         if (inputSource != null) {
    836             doc.setName(inputSource.getSystemId());
    837         }
    838
    839         return doc;
    840     }
    841
    842     private String getEncoding() {
    843         if (locator == null) {
    844             return null;
    845         }
    846
    847         // use reflection to avoid dependency on Locator2
    848         // or other locator implemenations.
    849         try {
    850             Method m = locator.getClass().getMethod("getEncoding",
    851                     new Class[] {});
    852
    853             if (m != null) {
    854                 return (String) m.invoke(locator, null);
    855             }
    856         } catch (Exception e) {
    857             // do nothing
    858         }
    859
    860         // couldn't determine encoding, returning null...
    861         return null;
    862     }
    863
    864     /***
    865      * a Strategy Method to determine if a given entity name is ignorable
    866      *
    867      * @param name
    868      *            DOCUMENT ME!
    869      *
    870      * @return DOCUMENT ME!
    871      */
    872     protected boolean isIgnorableEntity(String name) {
    873         return "amp".equals(name) || "apos".equals(name) || "gt".equals(name)
    874                 || "lt".equals(name) || "quot".equals(name);
    875     }
    876
    877     /***
    878      * Add all namespaces declared before the startElement() SAX event to the
    879      * current element so that they are available to child elements and
    880      * attributes
    881      *
    882      * @param element
    883      *            DOCUMENT ME!
    884      */
    885     protected void addDeclaredNamespaces(Element element) {
    886         Namespace elementNamespace = element.getNamespace();
    887
    888         for (int size = namespaceStack.size(); declaredNamespaceIndex < size;
    889                 declaredNamespaceIndex++) {
    890             Namespace namespace = namespaceStack
    891                     .getNamespace(declaredNamespaceIndex);
    892
    893             // if ( namespace != elementNamespace ) {
    894             element.add(namespace);
    895
    896             // }
    897         }
    898     }
    899
    900     /***
    901      * Add all the attributes to the given elements
    902      *
    903      * @param element
    904      *            DOCUMENT ME!
    905      * @param attributes
    906      *            DOCUMENT ME!
    907      */
    908     protected void addAttributes(Element element, Attributes attributes) {
    909         // XXXX: as an optimisation, we could deduce this value from the current
    910         // SAX parser settings, the SAX namespaces-prefixes feature
    911         boolean noNamespaceAttributes = false;
    912
    913         if (element instanceof AbstractElement) {
    914             // optimised method
    915             AbstractElement baseElement = (AbstractElement) element;
    916             baseElement.setAttributes(attributes, namespaceStack,
    917                     noNamespaceAttributes);
    918         } else {
    919             int size = attributes.getLength();
    920
    921             for (int i = 0; i < size; i++) {
    922                 String attributeQName = attributes.getQName(i);
    923
    924                 if (noNamespaceAttributes
    925                         || !attributeQName.startsWith("xmlns")) {
    926                     String attributeURI = attributes.getURI(i);
    927                     String attributeLocalName = attributes.getLocalName(i);
    928                     String attributeValue = attributes.getValue(i);
    929
    930                     QName qName = namespaceStack.getAttributeQName(
    931                             attributeURI, attributeLocalName, attributeQName);
    932                     element.addAttribute(qName, attributeValue);
    933                 }
    934             }
    935         }
    936     }
    937
    938     /***
    939      * Adds an internal DTD declaration to the list of declarations
    940      *
    941      * @param declaration
    942      *            DOCUMENT ME!
    943      */
    944     protected void addDTDDeclaration(Object declaration) {
    945         if (internalDTDDeclarations == null) {
    946             internalDTDDeclarations = new ArrayList();
    947         }
    948
    949         internalDTDDeclarations.add(declaration);
    950     }
    951
    952     /***
    953      * Adds an external DTD declaration to the list of declarations
    954      *
    955      * @param declaration
    956      *            DOCUMENT ME!
    957      */
    958     protected void addExternalDTDDeclaration(Object declaration) {
    959         if (externalDTDDeclarations == null) {
    960             externalDTDDeclarations = new ArrayList();
    961         }
    962
    963         externalDTDDeclarations.add(declaration);
    964     }
    965
    966     protected ElementStack createElementStack() {
    967         return new ElementStack();
    968     }
    969 }
    970
    971 /*
    972  * Redistribution and use of this software and associated documentation
    973  * ("Software"), with or without modification, are permitted provided that the
    974  * following conditions are met:
    975  *
    976  * 1. Redistributions of source code must retain copyright statements and
    977  * notices. Redistributions must also contain a copy of this document.
    978  *
    979  * 2. Redistributions in binary form must reproduce the above copyright notice,
    980  * this list of conditions and the following disclaimer in the documentation
    981  * and/or other materials provided with the distribution.
    982  *
    983  * 3. The name "DOM4J" must not be used to endorse or promote products derived
    984  * from this Software without prior written permission of MetaStuff, Ltd. For
    985  * written permission, please contact dom4j-info@metastuff.com.
    986  *
    987  * 4. Products derived from this Software may not be called "DOM4J" nor may
    988  * "DOM4J" appear in their names without prior written permission of MetaStuff,
    989  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
    990  *
    991  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
    992  *
    993  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
    994  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    995  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    996  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
    997  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    998  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    999  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    1000  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    1001  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    1002  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    1003  * POSSIBILITY OF SUCH DAMAGE.
    1004  *
    1005  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
    1006  */